WEB API: Difference between revisions

From VoIPmonitor.org
(Add curl --data-urlencode examples for getPCAP API to fix success:false errors)
(VG-3075: Document getVoipCalls response fields including new lastSIPresponseNum)
 
(18 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Important: Correct API Endpoint Path ==
= VoIPmonitor Web API =


All API requests MUST be sent to the correct endpoint path:
This page documents VoIPmonitor's Web APIs for programmatic access to CDR data, recordings, active calls, and system functions.


*  '''Correct:''' <code>/voipmonitor/php/api.php</code>
== Overview ==
*  '''Incorrect:''' <code>/voipmonitor/api.php</code>


Using the wrong path (missing the <code>/php/</code> directory) will cause API calls to fail with various errors.
<kroki lang="mermaid">
%%{init: {'flowchart': {'nodeSpacing': 15, 'rankSpacing': 30}}}%%
flowchart LR
    subgraph Client
        A[Application]
    end
    subgraph API["API Endpoints"]
        B["/php/api.php"]
        C["/php/model/sql.php"]
    end
    subgraph Tasks
        E[getVoipCalls]
        F[getVoiceRecording]
        G[getPCAP]
        H[listActiveCalls]
        I[getShareURL]
    end
    A -->|user/password| B
    A -->|Session cookie| C
    B --> E & F & G & H & I
</kroki>


If your VoIPmonitor GUI is installed in the default web root (/var/www/html), use:
{| class="wikitable"
curl -X POST 'http://yourserver/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD'
|-
! API !! Endpoint !! Authentication !! Use Case
|-
| '''HTTP API 2''' || <code>/php/api.php</code> || user/password params || CDR queries, recordings, PCAP, active calls
|-
| '''CDR HTTP API''' || <code>/php/model/sql.php</code> || Session cookie || Advanced CDR filtering with GUI filters
|}


If using a subdirectory like /demo:
{{Warning|1='''Correct path is critical:''' Always use <code>/php/api.php</code> (not <code>/api.php</code>). Missing the <code>/php/</code> directory causes "action parameter missing" errors.}}
curl -X POST 'http://yourserver/demo/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD'


== Custom Login ==
== HTTP API 2 ==


VoIPmonitor supports custom authentication mechanisms (such as LDAP) via the <code>custom_login.php</code> script. This script intercepts login requests from the web GUI and allows you to validate credentials against external systems.
Preferred API for programmatic access. Requests via HTTP POST or GET to <code>/php/api.php</code>.


'''Important:''' Web API (HTTP API 2) authentication uses local VoIPmonitor database credentials directly and does NOT use <code>custom_login.php</code> or LDAP authentication. API requests submit <code>user</code> and <code>password</code> parameters that are validated against the local database.
=== Authentication ===


=== LDAP Example ===
Include <code>user</code> and <code>password</code> parameters in every request:


Complete LDAP example available in GUI directory: scripts/ldap_custom_login_example.php.
<syntaxhighlight lang="bash">
curl 'http://server/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD&params=...'
</syntaxhighlight>


Ensure php-ldap package (or equivalent) is installed on the OS hosting the LDAP script.
{{Note|1=API authentication uses local VoIPmonitor database credentials. It does NOT use LDAP or custom_login.php.}}


==== Troubleshooting LDAP Login Issues ====
'''Rate Limiting:''' Enable in GUI > Settings > System Configuration > "API maximal concurrent connections".


If LDAP authentication is working for the local admin user but failing for non-admin users, you can debug the script manually from the command line to isolate LDAP connectivity issues.
=== getVoipCalls ===


===== Step 1: Enable Debug Mode for CLI Testing =====
Retrieve CDR records by search criteria.


Edit the <code>custom_login.php</code> file and **uncomment the debug block** at the top of the script (if present):
'''Parameters:'''
{| class="wikitable"
|-
! Parameter !! Description
|-
| <code>startTime</code> || Calls starting >= this time (required)
|-
| <code>startTimeTo</code> || Calls starting <= this time
|-
| <code>callEnd</code> || Calls ending <= this time
|-
| <code>caller</code> / <code>called</code> || Caller/called number
|-
| <code>callId</code> || SIP Call-ID
|-
| <code>cdrId</code> || Database ID
|-
| <code>id_sensor</code> || Sensor number
|-
| <code>msisdn</code> || Match caller OR called (instead of AND)
|-
| <code>onlyConnected</code> || <code>1</code> = only answered calls
|-
| <code>customHeaders</code> || Comma-separated custom header names to return
|}


<pre>
'''Examples:'''
# debug for manual run
if (!function_exists('debug_log')) {
function debug_log($txt) {
echo $txt . "\n";
}
}
# uncomment next line for test with manual run from command line (php SCRIPTNAME)
custom_login('testuser', 'testpass');
</pre>


===== Step 2: Run Script Manually =====
<syntaxhighlight lang="bash">
# HTTP GET - by time range and caller
curl 'http://server/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD&params={"startTime":"2024-01-01","startTimeTo":"2024-01-31","caller":"123456"}'


Execute the script directly from the command line:
# HTTP POST - by Call-ID
echo '{"task":"getVoipCalls","user":"USER","password":"PASSWORD","params":{"startTime":"2024-01-01","callId":"abc123"}}' | curl -X POST -d @- http://server/php/api.php


<syntaxhighlight lang="bash">
# With custom headers
cd /var/www/html/scripts/
curl -G 'http://server/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD' \
php custom_login.php
  --data-urlencode 'params={"startTime":"2024-01-01","caller":"123","customHeaders":"X-Custom-Header"}'
</syntaxhighlight>
</syntaxhighlight>
'''Response fields:'''
{| class="wikitable"
|-
! Field !! Description
|-
| <code>cdrId</code> || Database CDR ID
|-
| <code>id_sensor</code> || Sensor ID
|-
| <code>calldate</code> / <code>callend</code> || Call start/end time
|-
| <code>duration</code> / <code>connect_duration</code> || Total / connected duration
|-
| <code>caller</code> / <code>called</code> || Caller/called numbers
|-
| <code>sipcallerip</code> / <code>sipcalledip</code> || SIP endpoint IPs
|-
| <code>codec_a</code> / <code>codec_b</code> || Audio codecs
|-
| <code>lastSIPresponseNum</code> || '''(New in 2026.1)''' Final SIP response code (e.g., 200, 404, 503)
|-
| <code>callId</code> || SIP Call-ID
|}
=== getVoiceRecording ===


This will test the script with the hardcoded 'testuser' credentials and output any connection failure messages directly to the console.
Download audio recording (WAV format).
 
===== Step 3: Check LDAP Server Configuration =====
 
Verify the LDAP server configuration in your script:


* Check the <code>$ldapsrvs</code> variable contains correct LDAP server IP addresses
'''Parameters:'''
* Verify the LDAP server ports are correct and accessible
{| class="wikitable"
* Ensure network connectivity exists from the VoIPmonitor server to the LDAP endpoints
|-
! Parameter !! Description
|-
| <code>cdrId</code> || Database CDR ID (preferred)
|-
| <code>callId</code> || SIP Call-ID
|-
| <code>calldate</code> || Date hint (default: today)
|-
| <code>zip</code> || <code>true</code> = return ZIP archive
|-
| <code>ogg</code> || <code>true</code> = return OGG format
|-
| <code>saveaudio_afterconnect</code> || <code>"yes"</code> = audio only after call connect
|}


If the CLI test shows connection failures, verify network connectivity:
'''Examples:'''


<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
telnet <ldap_server_ip> <ldap_port>
# By CDR ID
# or
curl 'http://server/php/api.php?task=getVoiceRecording&user=USER&password=PASSWORD&params={"cdrId":12345}' > call.wav
nc -zv <ldap_server_ip> <ldap_port>
</syntaxhighlight>
 
===== Step 4: Verify Required Dependencies =====


Ensure the LDAP PHP extension is installed:
# By Call-ID
curl 'http://server/php/api.php?task=getVoiceRecording&user=USER&password=PASSWORD&params={"callId":"abc123"}' > call.wav


<syntaxhighlight lang="bash">
# Multiple recordings (returns ZIP)
# Debian/Ubuntu
echo '{"task":"getVoiceRecording","user":"USER","password":"PASSWORD","params":{"cdrId":[6,7]}}' | php api.php > calls.zip
php -m | grep ldap
sudo apt-get install php-ldap
 
# CentOS/RHEL
php -m | grep ldap
sudo yum install php-ldap
</syntaxhighlight>
</syntaxhighlight>


===== Step 5: Check User Mapping =====
=== getPCAP ===


The <code>custom_login</code> function must return a valid array containing a required numeric unique identifier (<code>id</code>). If your script successfully authenticates against LDAP but users cannot log in, verify:
Download PCAP file. Automatically merges multiple legs and returns ZIP if needed.


1. The user exists in the VoIPmonitor local database (<code>users</code> table)
'''Parameters:'''
2. The script correctly queries the local database to retrieve the user's ID
{| class="wikitable"
3. The returned array includes the <code>id</code> field
|-
! Parameter !! Description
|-
| <code>cdrId</code> || Database CDR ID
|-
| <code>callId</code> || SIP Call-ID
|-
| <code>cidInterval</code> || Time window (seconds) to search for matching Call-ID
|-
| <code>cidMerge</code> || <code>true</code> = merge multiple legs
|-
| <code>zip</code> || <code>true</code> = force ZIP output
|}


Create users in the GUI (Settings → Users) with the same usernames as in your LDAP directory if you require local user record mapping.
'''Examples:'''


===== Step 6: LDAP Users Cannot View CDRs Despite Being Assigned to Groups =====
<syntaxhighlight lang="bash">
# By CDR ID
curl 'http://server/php/api.php?task=getPCAP&user=USER&password=PASSWORD&params={"cdrId":"12345"}' > call.pcap


If LDAP users can authenticate successfully but cannot view CDRs even though they are assigned to groups with the correct permissions in the GUI, the issue is likely caused by the <code>is_admin</code> flag in your <code>custom_login.php</code> return array.
# By Call-ID with merge
curl -G 'http://server/php/api.php?task=getPCAP&user=USER&password=PASSWORD' \
  --data-urlencode 'params={"callId":"abc123","cidInterval":60,"cidMerge":true}'
</syntaxhighlight>


When <code>is_admin</code> is set to <code>false</code> (the default recommended setting), VoIPmonitor applies the group permissions from the user's local database record. However, if your script incorrectly includes <code>is_admin</code> with any value (even <code>false</code> itself), it may interfere with group-based permission inheritance depending on your specific configuration.
{{Tip|Use <code>curl -G --data-urlencode</code> for complex JSON params to avoid encoding issues.}}


To fix this issue:
=== listActiveCalls ===


1. Ensure your script returns the correct <code>id_group</code> value that matches the group assigned in the GUI Users & Audit section.
Get currently active calls from sensor.


2. Set <code>is_admin</code> to <code>false</code> (or omit it entirely to use default values) to allow group permissions to be applied correctly:
'''Parameters:''' <code>sensorId</code> (optional), <code>callId</code> (optional)
<pre>
return(array(
    'username' => $user,
    'is_admin' => false,  // Allows group permissions to be applied
    'id' => $userIdUniqueNum,
    'id_group' => $groupIdNum,  // Must match GUI group ID
    // ... other parameters
));
</pre>


3. Debug the <code>$groupIdNum</code> variable by running your script manually from the command line:
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
cd /var/www/html/scripts/
curl 'http://server/php/api.php?task=listActiveCalls&user=USER&password=PASSWORD&params={"sensorId":"1"}'
php custom_login.php
</syntaxhighlight>
</syntaxhighlight>
Check the debug output to verify that <code>$groupIdNum</code> is being assigned the correct integer value before the return statement.
4. Implement a fallback mechanism for users without a valid role to prevent login failures. For example:
<pre>
if ($groupIdNum === 0){
    debug_log("User $user does not have valid permissions to login, setting the group 1 with low permissions");
    $groupIdNum = 1;
}
</pre>
This ensures users without group mappings can still log in with restricted access rather than being completely blocked.
=== Custom Script ===
Custom login enables implementing your own mechanism. Create file: scripts/custom_login.php with function custom_login.
Location: <GUI_INSTALL_DIR>/scripts/custom_login.php
<?php
function custom_login($user, $password) {
        debug_log('custom_login');
        return(array(
                'username' => $user,
                'is_admin' => false,
                'id' => $userIdUniqueNum, // required numeric unique identifier for each user
// 'id_group' => 1, // you can set user rights with the gui's group id too
                'enable_sensors' => array(2,3)
        ));
}
?>
Returned array parameters:
*enable_sensors - array(A,B,...) where the number is number of the sensor. If no enable_sensors provided the user will see all sensors CDR.
*username
*name
*secret
*id_group
*group_name
*group_blocked
*can_cdr
*can_write_cdr
*can_play_audio
*can_download_audio
*can_listen_active_call
*can_show_fax
*can_pcap
*can_upload_pcap
*can_messages
*can_view_content_message
*can_view_message_url
*is_admin
*no_rtp
*simple_cdr
*hide_cdr_groups
*can_graphs
*can_tracker
*can_activecalls
*can_register
*can_sip_msg
*can_livesniffer
*can_capture_rules
*crules_remove_expire_time
*can_audit
*can_alerts_edit
*can_alerts_show_sent
*can_reports_edit
*can_reports_show_sent
*can_cdr_share_local_public
*can_cdr_share_local_private
*can_cdr_share_voipmonitor_org
*hide_license_information
*can_ipacc
*can_mtr
*can_show_sql_query
*can_sensors_operations
*show_only_connected_calls
*can_dashboard
*dashboard_read_only
*can_report_functions
*hide_change_password
*hide_user_configuration
*password_expired
*can_svg_paint
*can_3d_rtp_charts
*can_network
*can_edit_codebooks
*can_edit_all_templates
*can_delete_all_templates
*ip
*number
*domain
*vlan
*custom_headers_cdr
*custom_headers_message
*ip_number_domain_or
*note
*email
*blocked
*blocked_reason
*max_bad_login_attempt
*password_expiration_days
*enable_login_ip
*uc_color_theme_shift_h
*uc_color_theme_shift_s
*uc_color_theme_shift_v
*uc_color_theme_shift
*uc_font_main_menu
*uc_disable_confirm_before_unload
*uc_enable_dns_cdr
*uc_enable_dns_message
*uc_enable_flags_cdr_number
*uc_enable_flags_cdr_ip
*uc_enable_flags_message_number
*uc_enable_flags_message_ip
*uc_csv_field_separator
*count_audit_log
*req_2fa
== CDR HTTP API ==
Retrieves CDR rows in JSON, using WEB GUI filters: [[Call_Detail_Record_-_CDR#Filter_Form_button]].
Requires valid session: [[GUI_automate_login]]. Send parameters via POST.
Disable authorization (safe environments): '''GUI > Settings > System Configuration > Disable authorization for API usage'''.
=== CURL Notes ===
Use -G and --data-urlencode for special characters (e.g., + in caller).
Example:
curl -G -X GET 'http://localhost/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD' --data-urlencode 'params=[{"startTime":"2013-01-01","endTime":"2013-08-01","caller":"910251414414"},{"startTime":"2013-01-01","endTime":"2013-08-01","caller":"1+1","customHeaders":"Cell-ID-Caller,Cell-ID-Called"}]'
=== Input ===
HTTP POST address: php/model/sql.php
==== Mandatory Parameters ====
task:LISTING module:CDR
fdatefrom:DATE or fdateto:DATE is required due to mysql overloading
==== Suppress Results ====
suppress_results:1
will not list CDR but only {"total":"135"}
==== Datetime Range ====
fdatefrom:1986-01-09T00:00:00 fdateto:2013-05-28T00:00:00
==== Caller / Called Numbers ====
fcaller: caller num
fcalled: called num
fcallerd_type: 0 (if type is 0 searching is done for fcaller OR fcalled and value is taken from fcaller. If value is 1 searching is done for fcaller AND fcalled)
==== Domain ====
fcaller_domain: caller domain fcalled_domain: caller domain
fcallerd_domain_type:0 (if type is 0 searching is done for fcaller OR fcalled and value is taken from fcaller. If value is 1 searching is done for fcaller AND fcalled)
==== Caller ID Name ====
fcallername: caller name
==== SIP Caller / Called IP ====
fsipcallerip: caller SIP IP fsipcalledip: called SIP IP
fsipcallerdip_type:0 (if type is 0 searching is done for fcaller OR fcalled and value is taken from fcaller. If value is 1 searching is done for fcaller AND fcalled)
==== RTP Source / Destination IP ====
fa_saddr: 192.168.0.1
fb_saddr: 192.168.0.2
fab_saddr_type:0 (if type is 0 searching is done for fcaller OR fcalled and value is taken from fcaller. If value is 1 searching is done for fcaller AND fcalled)
==== Codecs ====
fcodec: 4, 12
list of numbers delimited by ','. Here is list of numbers and its codecs
define("PAYLOAD_PCMU", 0); define("PAYLOAD_GSM", 3); define("PAYLOAD_G723", 4); define("PAYLOAD_PCMA", 8); define("PAYLOAD_G722", 9); define("PAYLOAD_QCELP", 12); define("PAYLOAD_CN", 13); define("PAYLOAD_G729", 18); define("PAYLOAD_ILBC", 97); define("PAYLOAD_SPEEX", 98); define("PAYLOAD_SILK", 301); define("PAYLOAD_SILK8", 302); define("PAYLOAD_SILK12", 303); define("PAYLOAD_SILK16", 304); define("PAYLOAD_SILK24", 305); define("PAYLOAD_ISAC", 306); define("PAYLOAD_ISAC16", 307); define("PAYLOAD_ISAC32", 308); define("PAYLOAD_T38", 1000);
==== Call Duration ====
example: less than 30 and greater than 10 seconds
fdurationgt: 30 fdurationlt: 10
==== PDD ====
example: greater than 10 and less than 30 seconds
fpddgt: 10 fpddlt: 30
==== SIP Response Code ====
fsipresponse: 503
==== Interrupted Call ====
false or true
fbye:false
Direction (by trunk)
false or true
ftrunk:
SIP user agent
fa_ua: caller agent string fb_ua: called agent string
fab_ua_type:0 (if type is 0 searching is done for fcaller OR fcalled and value is taken from fcaller. If value is 1 searching is done for fcaller AND fcalled)
SIP Call-ID header
fcallid: string
SIP sensor ID (database ID)
fsensor_id:
==== Paging ====
page:1 start:0 limit:30
==== Ordering ====
sort:[{"property":"calldate2","direction":"DESC"}]
==== Special Ordering ====
orderByLoss: orderByDelay: orderByFixed1: orderByFixed2: orderByAdapt: orderByCallDuration_asc: orderByCallDuration_desc: orderBySrcSIP_IP_asc: orderBySrcSIP_IP_desc: orderByDstSIP_IP_asc: orderByDstSIP_IP_desc: orderBySrcNumber_asc: orderBySrcNumber_desc: orderByDstNumber_asc: orderByDstNumber_desc: orderByCallerName_asc: orderByCallerName_desc: orderByLastSIPrespNum_asc: orderByLastSIPrespNum_desc:
==== RTP ====
frtcp_maxjitter: frtcp_avgjitter: frtcp_maxfr: frtcp_avgfr: fmosf1: fmosf2: fmosadapt: f_d50: f_d70: f_d90: f_d120: f_d150: f_d200: f_d300: floss1: floss2: floss3: floss4: floss5: floss6: floss7: floss8: floss9: floss10: ffilterTemplate:
=== Output ===
JSON formatted array of CDR.
==== GUI in Default DocumentRoot ====
Example for /var/www/html
curl -X POST '192.168.76.201/php/model/sql.php?module=bypass_login&user=voipmonitor&pass=voipmonitor'
returns something like this
{"SID":"ahs2ubdhc0ukb262be60v900ko","cookie_name":"PHPSESSID","success":true,"_vm_version":240034,"_debug":false}
take the SID which you will put to cookie_name variable
curl -X POST -k --cookie "PHPSESSID=ahs2ubdhc0ukb262be60v900ko" "http://192.168.76.201/php/model/sql.php?task=LISTING&module=CDR&fdatefrom=2013-05-08T00:00:00&fcaller=190"
==== GUI in Demo Subdirectory ====
Example for /var/www/html/demo
curl -X POST '192.168.76.201/demo/php/model/sql.php?module=bypass_login&user=voipmonitor&pass=voipmonitor'
returns something like this
{"SID":"ahs2ubdhc0ukb262be60v900ko","cookie_name":"PHPSESSID-demo","success":true,"_vm_version":240034,"_debug":false}
take the SID which you will put to cookie_name variable
curl -X POST -k --cookie "PHPSESSID-demo=ahs2ubdhc0ukb262be60v900ko" "http://192.168.76.201/demo/php/model/sql.php?task=LISTING&module=CDR&fdatefrom=2013-05-08T00:00:00&fcaller=190"
==== Example Output for LISTING Task ====
See attached link: [[output of the API LISTING task]]
== Share CDR ==
Share CDR via request to php/model/utilities.php. For SIP+RTP, set type2 to "rtp"; for SIP only, type2:null.
=== Show Link Local Public ===
task=shareCdr&params={"type":"share_cdr_show","type2":null,"subType":"self_protected_link","id":[128024514],"emailFields":null}
=== Show Link Local Private ===
task=shareCdr&params={"type":"share_cdr_show","type2":null,"subType":"self_login_link","id":[128024514],"emailFields":null}
=== Show Link share.voipmonitor.org ===
task=shareCdr&params={"type":"share_cdr_show","type2":null,"subType":"share_link","id":[128024590],"emailFields":null}
=== Customizing share.voipmonitor.org Domain ===
To brand the share service with your own domain instead of using the default share.voipmonitor.org, edit the <code>brand.php</code> file in your GUI directory and add the following definitions:
<pre>
define('BRAND_SHARESITE', 'share.yourdomain.fr');
define('BRAND_DOMAIN', 'yourdomain.fr');
</pre>
Replace 'yourdomain.fr' with your actual domain. This causes share links to use your branded domain instead of share.voipmonitor.org.
== Get PCAP File ==
GET or POST request with CDR ID:
http://voipmonitor/php/pcap.php?id=203800251
Optionally filter out RTP: disable_rtp=1
== Search CDR by URL ==
Build URL for CDR display in browser:
http://localhost/admin.php?cdr_filter={fcallid:"uDR8mtloKFa1F8625VL2OXSFp.RuG73v"}
cdr_filter uses same arguments as [[WEB_API#CDR_HTTP_API]]
== HTTP API 2 ==
Preferred API for audio files by search criteria. Requests via HTTP POST or GET.
=== Rate Limit ===
Enable in '''GUI > Settings > System Configuration > API maximal concurrent connections'''. Limit per user.
=== getVoipCalls ===
==== Input Data ====
*startTime - all calls which started >= startTime
*startTimeTo - all calls which started <= startTimeTo (not mandatory)
*callEnd - all calls which ends <= callEnd (not mandatory)
*caller - caller number
*called - called number
*callId - Call-ID
*id_sensor - sensor's number
*msisdn - when you enter caller and called then cond is 'caller = 9999999 and called = 9999998'. With msisdn the cond is 'caller = 9999997 or called = 9999997'
*cdrId - ID number in db
*onlyConnected - 1 to get only connected calls (omit this parameter to retrieve all calls including unconnected)
*[custom header name]: [custom header value], seach cdrs by this custom header value
*customHeaders - the names of the returned custom header's values
task: getVoipCalls,
user: USER,
password: PASSWORD,
auditReason: reason text for audit log (not required),
params: {
  startTime: YYYY-MM-DD HH:II:SS,
  startTimeTo: YYYY-MM-DD HH:II:SS,
  callEnd: YYYY-MM-DD HH:II:SS,
  caller: 9999999,
  called: 9999999,
  callId: 'XXXXXXXXXXXXXXXXXXXXXX',
  id_sensor: 5,
  msisdn: 9999997,
  cdrId: 99999,
  onlyConnected: 1,  // Get only connected calls (omit this parameter for all calls including unconnected)
  [custom header name]: [custom header value],
  customHeaders: "Cell-ID-Caller,Cell-ID-Called"
}
==== Examples ====
===== HTTP POST Simple Parameter =====
echo '{"task": "getVoipCalls", "user": "USER", "password": "PASSWORD", "params": {"startTime": "2013-01-01", "endTime": "2013-08-01", "caller": "910251414414","customHeaders": "Cell-ID-Caller,Cell-ID-Called"}}' | php php/api.php
echo '{"task": "getVoipCalls", "user": "USER", "password": "PASSWORD", "params": {"startTime": "2023-01-01", "endTime": "2023-08-01", "callId": "a90eb404-4c12-363b-4812-56622343fbdf"}}' | php php/api.php
===== HTTP POST Array Parameter =====
echo '{"task": "getVoipCalls", "user": "USER", "password": "PASSWORD", "params": [{"startTime": "2013-01-01", "endTime": "2013-08-01", "caller": "910251414"},{"startTime": "2013-01-01", "endTime": "2013-08-01", "caller": "910251415", "customHeaders": "Cell-ID-Caller,Cell-ID-Called"}]}' | php php/api.php
===== HTTP GET Simple Parameter =====
http://localhost/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD&params={"startTime":"2013-01-01","endTime":"2013-08-01","caller":"910251414","customHeaders":"Cell-ID-Caller,Cell-ID-Called"}
===== HTTP GET Array Parameter =====
http://localhost/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD&params=[{"startTime":"2013-01-01","endTime":"2013-08-01","caller":"910251414"},{"startTime":"2013-01-01","endTime":"2013-08-01","caller":"910251415","customHeaders":"Cell-ID-Caller,Cell-ID-Called"}]
=== getVoiceRecording ===
==== Input Data ====
task: getVoiceRecording,
user: USER,
password: PASSWORD,
auditReason: reason text for audit log (not required),
params: {
      cdrId: 999999
      callId: 'XXXXXXXXXXXXXXXXXXXXXX',
      "CustomHeaderName": "CustomHeaderValue,
      calldate: '2015-03-01' //default value is current date, not used when cdrId set
}
or
params: { customHeader: 'name of the column in cdr_next table', customHeaderValue: 'your value', calldate: '2015-03-01' } //not recommended
===== Optional Parameters for Params =====
                  zip, ogg, : (true|false)
    saveaudio_afterconnect : ("yes"|"no")
==== Examples ====


===== HTTP POST =====
{{Note|1=IP addresses in response are integers. Convert with <code>INET_NTOA()</code> in MySQL or equivalent in your code.}}
 
echo '{"task": "getVoiceRecording", "user": "USER", "password": "PASSWORD", "params": {"cdrId": "4919"}}' | php api.php
echo '{"task": "getVoiceRecording", "user": "USER", "password": "PASSWORD", "params": {"cdrId": [6,7],"saveaudio_afterconnect":"yes"}}' | php api.php > /tmp/m6_m7.wav.zip
echo '{"task": "getVoiceRecording", "user": "USER", "password": "PASSWORD", "params": {"callId": "XXXXXXXXXXXXXXXXXXXXXX"}}' | php api.php
echo '{"task": "getVoiceRecording", "user": "USER", "password": "PASSWORD", "params": {"Cust_header": "CustValue"}}' | php api.php
 
===== HTTP GET =====
 
http://localhost/php/api.php?task=getVoiceRecording&user=USER&password=PASSWORD&params={"cdrId":4919}
http://localhost/php/api.php?task=getVoiceRecording&user=USER&password=PASSWORD&params={"cdrId":4919,"saveaudio_afterconnect":"yes"}
http://localhost/php/api.php?task=getVoiceRecording&user=USER&password=PASSWORD&params={"callId":"XXXXXXXXXXXXXXXXXXXXXX"}
http://localhost/php/api.php?task=getVoiceRecording&user=USER&password=PASSWORD&params={"callId":"XXXXXXXXXXXXXXXXXXXXXX","cidInterval":10}
http://localhost/php/api.php?task=getVoiceRecording&user=USER&password=PASSWORD&params={"callId":"XXXXXXXXXXXXXXXXXXXXXX","cidInterval":10,"saveaudio_afterconnect":"yes"}
http://localhost/php/api.php?task=getVoiceRecording&user=USER&password=PASSWORD&params={"Cust_header":"CustValue"}
 
=== listActiveCalls ===
 
==== Input Data ====
 
task: listActiveCalls,
user: USER,
password: PASSWORD,
params: {
    sensorId: 'sensor number',
    callId: 'callId'
}
sensorId and callId are optional.
 
==== Examples ====
 
===== HTTP POST =====
 
echo '{"task": "listActiveCalls", "user": "USER", "password": "PASSWORD", "params": {"sensorId": "1"}}' | php api.php
 
===== HTTP GET =====
 
http://localhost/php/api.php?task=listActiveCalls&user=USER&password=PASSWORD&params={"sensorId":"1"}


=== handleActiveCall ===
=== handleActiveCall ===


Start/stop RTP recording. Pausing saves empty RTP frames to PCAP; affects 'Listening to Active call' (no audio from paused calls).
Pause/unpause RTP recording on active call.


==== Input Data ====
'''Parameters:'''
* <code>sensorId</code> - Sensor number
* <code>command</code> - <code>pausecall</code> or <code>unpausecall</code>
* <code>callRef</code> - Call reference from listActiveCalls


task: handleActiveCall,
<syntaxhighlight lang="bash">
user: USER,
curl 'http://server/php/api.php?task=handleActiveCall&user=USER&password=PASSWORD&params={"sensorId":"1","command":"pausecall","callRef":"0x7f0e4c3c2680"}'
password: PASSWORD,
</syntaxhighlight>
auditReason: reason text for audit log (not required),
params: {
  sensorId: 'sensor number',
  command: 'command (can be only pausecall/unpausecall now)',
  callRef: 'call reference'
}


==== Examples ====
=== getShareURL ===


===== HTTP POST =====
Generate public shareable link for a CDR.


echo '{"task": "handleActiveCall", "user": "USER", "password": "PASSWORD", "params": {"sensorId": "1","command":"pausecall","callRef":"0x7f0e4c3c2680"}}' | php api.php
'''Parameters:'''
{| class="wikitable"
|-
! Parameter !! Description
|-
| <code>callId</code> or <code>cdrId</code> || Call identifier
|-
| <code>cidInterval</code> || Time window for Call-ID search
|-
| <code>rtp</code> || <code>true</code> = include RTP data
|-
| <code>sip_history</code> || <code>true</code> = SIP history only (no RTP)
|-
| <code>anonIps</code> || <code>true</code> = anonymize IPs
|-
| <code>validDays</code> || Link validity in days
|}


===== HTTP GET =====
<syntaxhighlight lang="bash">
 
curl 'http://server/php/api.php?task=getShareURL&user=USER&password=PASSWORD&params={"callId":"abc123","rtp":true,"validDays":15}'
http://localhost/php/api.php?task=handleActiveCall&user=USER&password=PASSWORD&params={"sensorId":"1","command":"pausecall","callRef":"0x7f0e4c3c2680"}
</syntaxhighlight>


=== reportSummary ===
=== reportSummary ===


Generates a pre-configured summary report as an image. This is the '''only''' type of report available via the web API.
Generate pre-configured summary report as image or JSON.


'''Important:''' Reports must be created in the GUI before use with the API.
{{Warning|1=Reports must be created in '''GUI → Reports → Daily Report''' BEFORE using the API. The <code>report_name</code> parameter must match the report's Description field.}}


====== Report Creation ======
'''Parameters:'''
* <code>report_name</code> - Description field from GUI report
* <code>datetime_from</code> / <code>datetime_to</code> - Date range
* <code>json</code> - <code>true</code> = return JSON instead of image


Before using this API task:
<syntaxhighlight lang="bash">
1. Navigate to '''GUI → Reports → Daily Report'''
curl 'http://server/php/api.php?task=reportSummary&user=USER&password=PASSWORD&params={"report_name":"Daily Summary","datetime_from":"2024-01-01","datetime_to":"2024-01-31"}'
2. Create a new report of type ''summary''
</syntaxhighlight>
3. Fill in the '''Description''' field - this text becomes your '''report_name''' for API calls
4. Configure any charts or filters as needed for the report
5. Save the report


The API generates reports that have been pre-configured in the GUI - you cannot create new report configurations via the API.
=== Other Tasks ===


==== Input Data ====
{| class="wikitable"
|-
! Task !! Description
|-
| <code>listCdrIds</code> || List CDR IDs with basic info. Params: <code>offset</code>, <code>size</code>
|-
| <code>getAudioGraph</code> || Get spectrogram/waveform image. Params: <code>cdrId</code>, <code>type</code> (S/P/ALL), <code>side</code> (L/R)
|}


task: reportSummary,
== CDR HTTP API ==
user: USER,
password: PASSWORD,
params: {
    report_name: '''the Description field from your GUI report''',
    datetime_from: datetime from (YYYY-MM-DD or YYYY-MM-DD HH:MM:SS)
    datetime_to: datetime to (YYYY-MM-DD or YYYY-MM-DD HH:MM:SS)
    json: true or false (returns JSON response instead of image)
}


==== Examples ====
Session-based API using GUI filter parameters. Requires login first.


===== HTTP POST =====
=== Authentication ===


echo '{"task": "reportSummary", "user": "USER", "password": "PASSWORD", "params": {"report_name": "test summary", "datetime_from": "2017-12-20", "datetime_to": "2017-12-20"}}' | php api.php
<syntaxhighlight lang="bash">
# Step 1: Login and get session
curl -X POST 'http://server/php/model/sql.php?module=bypass_login&user=USER&pass=PASSWORD'
# Returns: {"SID":"abc123...","cookie_name":"PHPSESSID","success":true}


===== HTTP GET =====
# Step 2: Use session cookie for requests
curl -X POST --cookie "PHPSESSID=abc123..." \
  'http://server/php/model/sql.php?task=LISTING&module=CDR&fdatefrom=2024-01-01T00:00:00&fcaller=123'
</syntaxhighlight>


http://localhost/php/api.php?task=reportSummary&user=USER&password=PASSWORD&params={"report_name":"test summary","datetime_from":"2017-12-20","datetime_to":"2017-12-20"}
{{Note|1=If GUI is in subdirectory (e.g., <code>/demo</code>), cookie name changes to <code>PHPSESSID-demo</code>.}}


=== getShareURL ===
'''Disable authorization:''' GUI > Settings > System Configuration > "Disable authorization for API usage"


Parameters like getVoiceRecording. Returns public link for LEGs by CID. Use '''callId''' (SIP Call-ID string) or '''cdrId''' (database ID) to identify the call. If 'sip_history' true, only SIP history (RTP ignored).
=== Filter Parameters ===


Options: 'anonIps' anonymizes IPs/domains; 'validDays' limits validity.
'''Mandatory:''' <code>task=LISTING</code>, <code>module=CDR</code>, and at least one of <code>fdatefrom</code> or <code>fdateto</code>.


==== Examples ====
{| class="wikitable"
|-
! Parameter !! Description !! Example
|-
| <code>fdatefrom</code> / <code>fdateto</code> || Date range || <code>2024-01-01T00:00:00</code>
|-
| <code>fcaller</code> / <code>fcalled</code> || Caller/called number || <code>123456</code>
|-
| <code>fcallerd_type</code> || <code>0</code>=OR, <code>1</code>=AND || <code>1</code>
|-
| <code>fcaller_domain</code> / <code>fcalled_domain</code> || SIP domain || <code>sip.example.com</code>
|-
| <code>fsipcallerip</code> / <code>fsipcalledip</code> || SIP IP address || <code>192.168.1.1</code>
|-
| <code>fcallid</code> || SIP Call-ID || <code>abc123</code>
|-
| <code>fsipresponse</code> || SIP response code || <code>503</code>
|-
| <code>fdurationgt</code> / <code>fdurationlt</code> || Duration (seconds) || <code>10</code>
|-
| <code>fsensor_id</code> || Sensor ID || <code>1</code>
|-
| <code>fcodec</code> || Codec numbers (comma-sep) || <code>0,8</code>
|-
| <code>suppress_results</code> || <code>1</code>=count only || <code>1</code>
|}


===== HTTP GET (with RTP, by SIP Call-ID) =====
'''Paging:''' <code>page</code>, <code>start</code>, <code>limit</code>


http://localhost/php/api.php?task=getShareURL&user=USER&password=PASSWORD&params={"callId":"1502262225","cidInterval":60,"rtp":true,"anonIps":true,"validDays":15}
'''Sorting:''' <code>sort=[{"property":"calldate","direction":"DESC"}]</code>
http://localhost/php/api.php?task=getShareURL&user=USER&password=PASSWORD&params={"callId":"1502262225","rtp":true}


===== HTTP GET (by database ID/CdrId) =====
{{Warning|1=Use <code>=</code> (equals) for parameters, not <code>:</code> (colon). Example: <code>fcallerd_type=1</code> (correct), not <code>fcallerd_type:1</code> (wrong).}}


curl -G -X POST 'http://localhost/php/api.php?task=getShareURL&user=USER&password=PASSWORD' --data-urlencode 'params={"cdrId":"12345","rtp":true}'
=== Wildcards ===


This returns a URL in the format: http://localhost/master/cdr.php?cdrId=12345&hash=HASH_VALUE
Use <code>%25</code> (URL-encoded <code>%</code>) for SQL LIKE patterns:


===== HTTP GET (only SIP) =====
<syntaxhighlight lang="bash">
# Caller starting with "00"
curl -G 'http://server/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD' \
  --data-urlencode 'params={"startTime":"2024-01-01","caller":"%2500%25"}'
</syntaxhighlight>


http://localhost/php/api.php?task=getShareURL&user=USER&password=PASSWORD&params={"callId":"1502262225","cidInterval":60}
=== Codec Values ===
http://localhost/php/api.php?task=getShareURL&user=USER&password=PASSWORD&params={"callId":"1502262225"}


===== HTTP GET (SIP History) =====
{| class="wikitable"
|-
! Value !! Codec !! Value !! Codec
|-
| 0 || PCMU (G.711 μ-law) || 8 || PCMA (G.711 A-law)
|-
| 3 || GSM || 9 || G.722
|-
| 4 || G.723 || 12 || QCELP
|-
| 18 || G.729 || 97 || iLBC
|-
| 98 || Speex || 301-305 || SILK variants
|-
| 306-308 || iSAC variants || 1000 || T.38 (Fax)
|}


http://localhost/php/api.php?task=getShareURL&user=USER&password=PASSWORD&params={"callId":"1502262225","cidInterval":60, "sip_history": true}
=== RTP Quality Filters ===
http://localhost/php/api.php?task=getShareURL&user=USER&password=PASSWORD&params={"callId":"1502262225","sip_history": true}


=== getPCAP ===
{| class="wikitable"
|-
! Parameter !! Description
|-
| <code>fmosf1</code>, <code>fmosf2</code>, <code>fmosadapt</code> || MOS score filters
|-
| <code>frtcp_maxjitter</code>, <code>frtcp_avgjitter</code> || RTCP jitter
|-
| <code>frtcp_maxfr</code>, <code>frtcp_avgfr</code> || RTCP frame rate
|-
| <code>floss1</code> - <code>floss10</code> || Packet loss distribution
|-
| <code>f_d50</code>, <code>f_d70</code>, <code>f_d90</code>, ... || Delay distribution (50ms, 70ms, etc.)
|}


Parameters like getVoiceRecording. Returns PCAP (merged legs if multiple in interval). Zip auto if multiple.
== Utility Endpoints ==


==== curl with data-urlencode ===
=== Direct PCAP Download ===


Use <code>-G</code> and <code>--data-urlencode</code> to properly encode the params string, especially when it contains special characters or complex bracket structures. This prevents <code>success:false</code> responses due to malformed parameters.
<syntaxhighlight lang="bash">
curl 'http://server/php/pcap.php?id=12345'          # Full PCAP
curl 'http://server/php/pcap.php?id=12345&disable_rtp=1'  # SIP only
</syntaxhighlight>


===== HTTP GET (with cURL recommended) =====
=== CDR URL for Browser ===


curl -G -X GET 'http://localhost/php/api.php?task=getPCAP&user=USER&password=PASSWORD' --data-urlencode 'params={"callId":"1502262225","cidInterval":60,"cidMerge":true}'
<syntaxhighlight lang="text">
http://server/admin.php?cdr_filter={fcallid:"abc123"}
</syntaxhighlight>


===== HTTP POST (with cURL recommended for complex params) =====
=== SIP History ===


curl -X POST 'http://localhost/php/api.php?task=getPCAP&user=USER&password=PASSWORD' \
Requires session authentication first.
  --data 'params={"callId":"1502262225","cidInterval":60}' \
  --data 'cidMerge=true' \
  --data 'zip=true'


==== Direct URL Examples (GET) =====
<syntaxhighlight lang="bash">
# JSON data
curl --cookie "PHPSESSID=..." 'http://server/php/pcap2text.php?action=brief_data&id=12345'


These work for simple cases but may fail with special characters:
# HTML table
curl --cookie "PHPSESSID=..." 'http://server/php/pcap2text.php?action=brief&id=12345'


http://localhost/php/api.php?task=getPCAP&user=USER&password=PASSWORD&params={"cdrId":"76"}
# MSC diagram
http://localhost/php/api.php?task=getPCAP&user=USER&password=PASSWORD&params={"callId":"1502262225"}
curl --cookie "PHPSESSID=..." 'http://server/php/pcap2text.php?action=getMSC&id=12345'
http://localhost/php/api.php?task=getPCAP&user=USER&password=PASSWORD&params={"callId":"1502262225","cidInterval":60}
</syntaxhighlight>


=== listCdrIds ===
=== License Check ===


Returns 'size' CDR records with basic info, starting from 'offset' ID.
<syntaxhighlight lang="bash">
# Basic license check
curl 'http://server/php/apilicensecheck.php?task=licenseCheck'


==== Input Data ====
# Concurrent calls limit check
curl 'http://server/php/apilicensecheck.php?task=licenseCallsLimitCheck'
</syntaxhighlight>


task: listCdrIds,
== Share CDR Configuration ==
user: USER,
password: PASSWORD,
flags: number, //not required, bitfield format: 0x01 .. simpleOutput, 0x02 .. date in ISO8601 format
params: {
      offset: starting cdr id,
      size: number of cdr records to return
}}


==== Example ====
=== Share Link Types ===


echo '{"task": "listCdrIds", "user": "USER", "password": "PASSWORD", "params": {"offset":"offset_num","size":"number of cdr records"}}' | php api.php
Use <code>/php/model/utilities.php</code> with <code>task=shareCdr</code>:


==== Request ====
{| class="wikitable"
|-
! Type !! subType !! Description
|-
| Local Public || <code>self_protected_link</code> || Password-protected local link
|-
| Local Private || <code>self_login_link</code> || Requires GUI login
|-
| voipmonitor.org || <code>share_link</code> || Public via share.voipmonitor.org
|}


task: listCdrIds,
=== Custom Branding ===
user: USER,
password: PASSWORD,
flags: number, //not required, bitfield format: 0x01 .. simpleOutput, 0x02 .. date in ISO8601 format
params: json_encode: {
          offset: starting cdr id,
          size: number of cdr records to return
}


==== Example ====
'''Product Name:''' Edit <code>config/system_configuration.php</code>:
<syntaxhighlight lang="php">
define('BRAND_NAME', 'YourCompany');
</syntaxhighlight>


http://localhost/php/api.php?task=listCdrIds&user=USER&password=PASSWORD&params={"offset":"offset_num","size":"number_of_cdr_records"}
'''Share Domain:''' Edit <code>brand.php</code> to use your domain instead of share.voipmonitor.org:
<syntaxhighlight lang="php">
define('BRAND_SHARESITE', 'share.yourdomain.com');
define('BRAND_DOMAIN', 'yourdomain.com');
</syntaxhighlight>


=== getAudioGraph ===
== Custom Login (LDAP/SSO) ==


==== Input Data ====
Custom authentication via <code>scripts/custom_login.php</code>. This applies to GUI login only, NOT to API authentication.


task: getAudioGraph,
=== Basic Structure ===
user: USER,
password: PASSWORD,
params: {
    cdrId : number (CDR.id),
    type : S/P/ALL ( Spectrogram / Peaks=Waveform / ALL=including both types and both sides )
    side : L/R ( Left / Right )
    height: px
generating: 1 / 0 ( optional param, default 0, tells if audiograph data can be generated from packets in a spooldir. This parameter is needed if you don't use 'save_audiograph = yes' in the sensor config. If generating is 0 then the api call is looking only for created audiographs.)
}


==== Output ====
<syntaxhighlight lang="php">
<?php
function custom_login($user, $password) {
    // Authenticate against external system (LDAP, etc.)
    // ...


Depends on 'type' (zip if "type":"ALL"):
    return array(
        'username' => $user,
        'id' => $uniqueNumericId,  // REQUIRED: Must be unique per user!
        'is_admin' => false,
        'id_group' => 1,          // Optional: GUI group ID
        'enable_sensors' => array(1,2,3)  // Optional: restrict to sensors
    );
}
?>
</syntaxhighlight>


PNG image
{{Warning|1='''The <code>id</code> field MUST be unique per user.''' If multiple users return the same ID, they share ALL settings (timezone, dashboard, etc.). Use LDAP <code>uidnumber</code> or <code>crc32($email)</code> for uniqueness.}}
zip file


==== Example ====
=== LDAP Example ===


http://192.168.88.46/voipmonitor-git/gui/php/api.php?task=getAudioGraph&user=USER&password=PASSWORD&params={"cdrId":191,"type":"S","side":"R","height":"100"}
See <code>scripts/ldap_custom_login_example.php</code> in GUI directory.


== Direct Links ==
'''Requirements:''' <code>php-ldap</code> package installed.


Active calls: index.php?activecalls=1&hidegrid=1&hidemenu=1
'''Debug from CLI:'''
<syntaxhighlight lang="bash">
cd /var/www/html/scripts/
php custom_login.php
</syntaxhighlight>


== SIP History ==
Enable debug by uncommenting the debug block at top of script.


Retrieves SIP history in various forms.
=== Troubleshooting ===


=== Parameters ===
'''Users share settings:''' Return unique <code>id</code> per user (see warning above).


action: action., currently brief_data, brief, getMSC actions. required
'''LDAP users can't view CDRs:''' Ensure <code>is_admin => false</code> and correct <code>id_group</code> is returned.
id: cdr id of the call, required
dns_lookup: 0|1 ... make dns lookup
ip_not_allowed: exclude this ips from the output (possibility to hide some internal information)


=== Example of Usage ===
'''Script being deleted:''' GUI antivirus deletes scripts with <code>shell_exec()</code>, <code>exec()</code>, etc.


# first get auth session
'''Solutions:'''
curl -X POST '192.168.76.201/php/model/sql.php?module=bypass_login&user=voipmonitor&pass=voipmonitor'
# Disable antivirus in <code>config/system_configuration.php</code>:
{"SID":"ahs2ubdhc0ukb262be60v900ko","cookie_name":"PHPSESSID","success":true,"_vm_version":240034,"_debug":false}
<syntaxhighlight lang="php">
 
define('DISABLE_ANTIVIRUS', true);
* 'brief_data' action get SIP history data in JSON format
</syntaxhighlight>
curl -X POST -k --cookie "PHPSESSID=ahs2ubdhc0ukb262be60v900ko" "http://192.168.76.201/php/pcap2text.php?action=brief_data&id=677&dns_lookup=0&ip_not_allowed=10.1.1.1,10.2.2.0/24
# Or move shell commands to external file outside web directory and <code>require_once()</code> it.
{"results":[{"num":"1","time":0,"src":"80.92.240.130","dst":"185.71.40.210","srcip":"80.92.240.130","dstip":"185.71.40.210","srcport":"5060","dstport":"5060","packet_len":"1046","direction":"-->","protocol":"SIP\/SDP","spec_type":null,"msg":"INVITE sip:2260@10.133.67.250:5060 len:1046","type":"request"},{"num":"2","time":0.054963111877441406,"src":"185.71.40.210","dst":"80.92.240.130","srcip":"185.71.40.210","dstip":"80.92.240.130","srcport":"5060","dstport":"5060","packet_len":"437","direction":"-->","protocol":"SIP","spec_type":null,"msg":"100 Trying len:437","type":"response"},{"num":"3","time":0.9610240459442139,"src":"185.71.40.210","dst":"80.92.240.130","srcip":"185.71.40.210","dstip":"80.92.240.130","srcport":"5060","dstport":"5060","packet_len":"588","direction":"-->","protocol":"SIP","spec_type":null,"msg":"180 Ringing len:588","type":"response"},{"num":"4","time":28.761795043945312,"src":"80.92.240.130","dst":"185.71.40.210","srcip":"80.92.240.130","dstip":"185.71.40.210","srcport":"5060","dstport":"5060","packet_len":"412","direction":"-->","protocol":"SIP","spec_type":null,"msg":"CANCEL sip:2260@10.133.67.250:5060 len:412","type":"request"},{"num":"5","time":28.795578002929688,"src":"185.71.40.210","dst":"80.92.240.130","srcip":"185.71.40.210","dstip":"80.92.240.130","srcport":"5060","dstport":"5060","packet_len":"433","direction":"-->","protocol":"SIP","spec_type":null,"msg":"200 OK len:433","type":"response"},{"num":"6","time":28.800518035888672,"src":"185.71.40.210","dst":"80.92.240.130","srcip":"185.71.40.210","dstip":"80.92.240.130","srcport":"5060","dstport":"5060","packet_len":"448","direction":"-->","protocol":"SIP","spec_type":null,"msg":"487 Request Cancelled len:448","type":"response"},{"num":"7","time":28.811052083969116,"src":"80.92.240.130","dst":"185.71.40.210","srcip":"80.92.240.130","dstip":"185.71.40.210","srcport":"5060","dstport":"5060","packet_len":"466","direction":"-->","protocol":"SIP","spec_type":null,"msg":"ACK sip:226@10.133.67.250:5060 len:466","type":"request"}],"total":7,"errors":{},"success":true,"_debug":false}
 
* 'brief' action get SIP history in simple HTML format (as table tag)
curl -X POST -k --cookie "PHPSESSID=ahs2ubdhc0ukb262be60v900ko" "http://192.168.76.201/php/pcap2text.php?action=brief&id=677&dns_lookup=0&ip_not_allowed=10.1.1.1,10.2.2.0/24
 
* 'genMSC' action get SIP history as diagram in HTML format
curl -X POST -k --cookie "PHPSESSID=ahs2ubdhc0ukb262be60v900ko" "http://192.168.76.201/php/pcap2text.php?action=getMSC&id=677&dns_lookup=0&ip_not_allowed=10.1.1.1,10.2.2.0/24


== Check License ==
=== Azure AD / Microsoft SSO ===


* Basic test for license (covers ionCube issues):
For Microsoft Entra ID integration, see [[Microsoft_Sign_in_usage]]. For Google, see [[Google_Sign_in_usage]].


'http://localhost:88/php/apilicensecheck.php?task=licenseCheck'
=== Return Array Parameters ===
{"status":1,"message":"license file key.php expired. Current date: 2021-02-23 Expiration date: 2021-02-16"}
'http://localhost:88/php/apilicensecheck.php?task=licenseCheck'
{"status":0,"message":"License OK."}


* Test concurrent calls limit (requires functional ionCube):
Full list of available return parameters for custom_login:


'http://localhost:88/php/apilicensecheck.php?task=licenseCallsLimitCheck'
<div class="mw-collapsible mw-collapsed">
 
<syntaxhighlight lang="text">
Examples:
username, name, id, id_group, group_name, is_admin, email
 
enable_sensors - array of sensor IDs user can access
{"status":1,"message":"You have exceeded the license limit for concurrent calls and the system has been locked. Contact support."}
can_cdr, can_write_cdr, can_play_audio, can_download_audio
{"status":1,"message":"You have exceeded the license limit for concurrent calls. The system will be locked in 14 days. Contact support."}
can_listen_active_call, can_pcap, can_upload_pcap
{"status":0,"message":"Limit violation for 2nd consecutive day."}
can_messages, can_view_content_message, can_graphs
 
can_activecalls, can_register, can_sip_msg, can_livesniffer
=== Resolving License Lockout ===
can_capture_rules, can_audit, can_alerts_edit, can_reports_edit
 
can_dashboard, can_ipacc, can_mtr, can_sensors_operations
If the GUI is locked due to exceeding the licensed channel limit for more than two consecutive days, you have the following options:
can_network, can_edit_codebooks, hide_license_information
 
ip, number, domain, vlan (user restrictions)
'''Manually Delete CDRs from Database (Self-Service Option)'''
custom_headers_cdr, custom_headers_message
 
blocked, blocked_reason, req_2fa
Reduce the concurrent call count by deleting old CDR records from the database. This can be done directly via SQL:
 
<syntaxhighlight lang="sql">
-- Warning: This permanently deletes call records
-- Always backup before running DELETE operations
 
-- Delete CDRs older than a specific date (adjust date as needed)
DELETE FROM cdr WHERE calldate < '2024-01-01';
 
-- Delete specific CDRs by ID range
DELETE FROM cdr WHERE ID BETWEEN 1000 AND 5000;
</syntaxhighlight>
</syntaxhighlight>
</div>


After deleting CDRs, the system may need time to recalculate the concurrent call count. Monitor the license status using the API endpoint above.
== See Also ==
 
'''Important Notes:'''
* Deleting CDRs is permanent and cannot be undone
* This only affects CDR records in the database, not captured audio files (PCAPs)
* The concurrent call count is based on recent CDR data; deleting older records may reduce the count below the license limit
* Always perform a full database backup before manual CDR deletion
 
'''Upgrade License Tier'''
 
Contact VoIPmonitor support to upgrade your license to a higher channel count tier:
 
* Higher-tier licenses support more concurrent calls
* Upgrading prevents future lockout issues
* Contact via the customer portal or email to request a license upgrade
 
'''Contact Support for Lockout Removal'''


If you need immediate unlock without deleting CDRs:
* [[Call_Detail_Record_-_CDR#Filter_Form_button|CDR Filter Form]] - GUI filter options
* [[Active_calls|Active Calls]] - Active calls monitoring
* [[User_Management|User Management]] - User permissions
* [[Google_Sign_in_usage|Google Sign-In]] - Google OAuth setup
* [[Microsoft_Sign_in_usage|Microsoft Sign-In]] - Azure AD setup


* Contact VoIPmonitor support via customer portal or email
* State that the system is locked due to exceeding the concurrent call limit
* Support can temporarily increase the limit or clear the lock on their end
* After support clears the lock, verify via Tools > System Status > License and GUI status


=== AI Summary for RAG ===
== AI Summary for RAG ==


'''Summary:''' This article covers VoIPmonitor's custom login (LDAP/script examples, parameters), CDR HTTP API (inputs/outputs, examples), sharing CDRs, getting PCAPs, URL searches, HTTP API 2 (getVoipCalls, recordings, active calls, reports, shares, PCAPs, CDR lists, audio graphs), direct links, SIP history retrieval, license checks, and resolving license lockout. The LDAP troubleshooting section provides step-by-step instructions for debugging LDAP login issues by testing the custom_login.php script from the command line. A new troubleshooting subsection addresses the specific issue where LDAP users can authenticate but cannot view CDRs despite being assigned to groups with correct permissions, explaining how to properly configure the is_admin flag and id_group variable. The license lockout resolution section explains three options when the GUI is locked due to exceeding the licensed channel limit: manually deleting CDRs from the database (self-service), upgrading the license tier, or contacting support for immediate lockout removal. The getPCAP documentation now includes important troubleshooting guidance on using curl --data-urlencode to properly encode params strings, which is a common solution for API requests that return success:false responses.
'''Summary:''' VoIPmonitor Web API reference with two main APIs: (1) HTTP API 2 at <code>/php/api.php</code> using user/password authentication for tasks like getVoipCalls, getVoiceRecording, getPCAP, listActiveCalls, getShareURL, handleActiveCall, reportSummary; (2) CDR HTTP API at <code>/php/model/sql.php</code> using session cookies with full GUI filter support. Critical: correct endpoint path is <code>/php/api.php</code> (not <code>/api.php</code>). reportSummary requires pre-configured reports in GUI. Custom login via <code>scripts/custom_login.php</code> enables LDAP/SSO - requires unique numeric ID per user to avoid shared settings. Antivirus may delete scripts with shell_exec - disable with DISABLE_ANTIVIRUS or move to external file. Branding: BRAND_NAME for product name, BRAND_SHARESITE/BRAND_DOMAIN in brand.php for custom share domain.


'''Keywords:''' custom login, LDAP, LDAP troubleshooting, CLI testing, group permissions, is_admin, id_group, LDAP users cannot view CDR, groupIdNum, CDR API, HTTP API, getVoipCalls, getVoiceRecording, active calls, reportSummary, getShareURL, getPCAP, PCAP download, success:false API error, curl data-urlencode, URL encoding, params encoding, PCAP API curl example, SIP history, license check, license lockout, concurrent calls limit, delete CDRs manually, license upgrade
'''Keywords:''' web api, http api, api.php, sql.php, getVoipCalls, getVoiceRecording, getPCAP, getShareURL, listActiveCalls, reportSummary, handleActiveCall, CDR filter, custom login, LDAP, authentication, session, curl, JSON, branding, BRAND_NAME, BRAND_SHARESITE, antivirus, DISABLE_ANTIVIRUS, unique user id, uidnumber


'''Key Questions:'''
'''Key Questions:'''
* How to implement custom login in VoIPmonitor?
* What is the correct VoIPmonitor API endpoint path?
* How to debug LDAP login issues from the command line?
* How do I retrieve CDRs via the VoIPmonitor API?
* What to do when non-admin users cannot log in via LDAP?
* How to download PCAP or voice recordings via API?
* How to test LDAP connectivity in custom_login.php?
* How to list active calls via API?
* Why can LDAP users authenticate but not view CDRs even with correct group permissions?
* How to generate reports via API?
* How does the is_admin flag affect group permissions in custom_login.php?
* How to configure LDAP authentication for VoIPmonitor?
* How to set up id_group for LDAP users to inherit GUI group permissions?
* Why do LDAP users share settings? (need unique ID)
* What is the fallback mechanism for users without valid group roles?
* Why is custom_login.php being deleted? (antivirus)
* What parameters does the CDR HTTP API support?
* How to customize VoIPmonitor branding?
* How to share CDRs via API?
* What's the difference between api.php and sql.php?
* What is HTTP API 2 and its methods?
* How to use wildcards in API filter parameters?
* How to retrieve PCAP files via API?
* Why does getPCAP API return success:false?
* How to use curl --data-urlencode with getPCAP API?
* What is the correct curl syntax for getPCAP endpoint?
* How to URL encode params string in API requests?
* How to retrieve SIP history?
* How to check VoIPmonitor license via API?
* How to resolve GUI lockout due to exceeding licensed channel limit?
* Can I manually delete CDRs to unlock a locked VoIPmonitor license?
* How do I upgrade my VoIPmonitor license tier?
* What are the options when the license concurrent calls limit is exceeded?

Latest revision as of 12:16, 19 January 2026

VoIPmonitor Web API

This page documents VoIPmonitor's Web APIs for programmatic access to CDR data, recordings, active calls, and system functions.

Overview

API Endpoint Authentication Use Case
HTTP API 2 /php/api.php user/password params CDR queries, recordings, PCAP, active calls
CDR HTTP API /php/model/sql.php Session cookie Advanced CDR filtering with GUI filters

⚠️ Warning: Correct path is critical: Always use /php/api.php (not /api.php). Missing the /php/ directory causes "action parameter missing" errors.

HTTP API 2

Preferred API for programmatic access. Requests via HTTP POST or GET to /php/api.php.

Authentication

Include user and password parameters in every request:

curl 'http://server/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD&params=...'

ℹ️ Note: API authentication uses local VoIPmonitor database credentials. It does NOT use LDAP or custom_login.php.

Rate Limiting: Enable in GUI > Settings > System Configuration > "API maximal concurrent connections".

getVoipCalls

Retrieve CDR records by search criteria.

Parameters:

Parameter Description
startTime Calls starting >= this time (required)
startTimeTo Calls starting <= this time
callEnd Calls ending <= this time
caller / called Caller/called number
callId SIP Call-ID
cdrId Database ID
id_sensor Sensor number
msisdn Match caller OR called (instead of AND)
onlyConnected 1 = only answered calls
customHeaders Comma-separated custom header names to return

Examples:

# HTTP GET - by time range and caller
curl 'http://server/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD&params={"startTime":"2024-01-01","startTimeTo":"2024-01-31","caller":"123456"}'

# HTTP POST - by Call-ID
echo '{"task":"getVoipCalls","user":"USER","password":"PASSWORD","params":{"startTime":"2024-01-01","callId":"abc123"}}' | curl -X POST -d @- http://server/php/api.php

# With custom headers
curl -G 'http://server/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD' \
  --data-urlencode 'params={"startTime":"2024-01-01","caller":"123","customHeaders":"X-Custom-Header"}'

Response fields:

Field Description
cdrId Database CDR ID
id_sensor Sensor ID
calldate / callend Call start/end time
duration / connect_duration Total / connected duration
caller / called Caller/called numbers
sipcallerip / sipcalledip SIP endpoint IPs
codec_a / codec_b Audio codecs
lastSIPresponseNum (New in 2026.1) Final SIP response code (e.g., 200, 404, 503)
callId SIP Call-ID

getVoiceRecording

Download audio recording (WAV format).

Parameters:

Parameter Description
cdrId Database CDR ID (preferred)
callId SIP Call-ID
calldate Date hint (default: today)
zip true = return ZIP archive
ogg true = return OGG format
saveaudio_afterconnect "yes" = audio only after call connect

Examples:

# By CDR ID
curl 'http://server/php/api.php?task=getVoiceRecording&user=USER&password=PASSWORD&params={"cdrId":12345}' > call.wav

# By Call-ID
curl 'http://server/php/api.php?task=getVoiceRecording&user=USER&password=PASSWORD&params={"callId":"abc123"}' > call.wav

# Multiple recordings (returns ZIP)
echo '{"task":"getVoiceRecording","user":"USER","password":"PASSWORD","params":{"cdrId":[6,7]}}' | php api.php > calls.zip

getPCAP

Download PCAP file. Automatically merges multiple legs and returns ZIP if needed.

Parameters:

Parameter Description
cdrId Database CDR ID
callId SIP Call-ID
cidInterval Time window (seconds) to search for matching Call-ID
cidMerge true = merge multiple legs
zip true = force ZIP output

Examples:

# By CDR ID
curl 'http://server/php/api.php?task=getPCAP&user=USER&password=PASSWORD&params={"cdrId":"12345"}' > call.pcap

# By Call-ID with merge
curl -G 'http://server/php/api.php?task=getPCAP&user=USER&password=PASSWORD' \
  --data-urlencode 'params={"callId":"abc123","cidInterval":60,"cidMerge":true}'

💡 Tip: Use curl -G --data-urlencode for complex JSON params to avoid encoding issues.

listActiveCalls

Get currently active calls from sensor.

Parameters: sensorId (optional), callId (optional)

curl 'http://server/php/api.php?task=listActiveCalls&user=USER&password=PASSWORD&params={"sensorId":"1"}'

ℹ️ Note: IP addresses in response are integers. Convert with INET_NTOA() in MySQL or equivalent in your code.

handleActiveCall

Pause/unpause RTP recording on active call.

Parameters:

  • sensorId - Sensor number
  • command - pausecall or unpausecall
  • callRef - Call reference from listActiveCalls
curl 'http://server/php/api.php?task=handleActiveCall&user=USER&password=PASSWORD&params={"sensorId":"1","command":"pausecall","callRef":"0x7f0e4c3c2680"}'

getShareURL

Generate public shareable link for a CDR.

Parameters:

Parameter Description
callId or cdrId Call identifier
cidInterval Time window for Call-ID search
rtp true = include RTP data
sip_history true = SIP history only (no RTP)
anonIps true = anonymize IPs
validDays Link validity in days
curl 'http://server/php/api.php?task=getShareURL&user=USER&password=PASSWORD&params={"callId":"abc123","rtp":true,"validDays":15}'

reportSummary

Generate pre-configured summary report as image or JSON.

⚠️ Warning: Reports must be created in GUI → Reports → Daily Report BEFORE using the API. The report_name parameter must match the report's Description field.

Parameters:

  • report_name - Description field from GUI report
  • datetime_from / datetime_to - Date range
  • json - true = return JSON instead of image
curl 'http://server/php/api.php?task=reportSummary&user=USER&password=PASSWORD&params={"report_name":"Daily Summary","datetime_from":"2024-01-01","datetime_to":"2024-01-31"}'

Other Tasks

Task Description
listCdrIds List CDR IDs with basic info. Params: offset, size
getAudioGraph Get spectrogram/waveform image. Params: cdrId, type (S/P/ALL), side (L/R)

CDR HTTP API

Session-based API using GUI filter parameters. Requires login first.

Authentication

# Step 1: Login and get session
curl -X POST 'http://server/php/model/sql.php?module=bypass_login&user=USER&pass=PASSWORD'
# Returns: {"SID":"abc123...","cookie_name":"PHPSESSID","success":true}

# Step 2: Use session cookie for requests
curl -X POST --cookie "PHPSESSID=abc123..." \
  'http://server/php/model/sql.php?task=LISTING&module=CDR&fdatefrom=2024-01-01T00:00:00&fcaller=123'

ℹ️ Note: If GUI is in subdirectory (e.g., /demo), cookie name changes to PHPSESSID-demo.

Disable authorization: GUI > Settings > System Configuration > "Disable authorization for API usage"

Filter Parameters

Mandatory: task=LISTING, module=CDR, and at least one of fdatefrom or fdateto.

Parameter Description Example
fdatefrom / fdateto Date range 2024-01-01T00:00:00
fcaller / fcalled Caller/called number 123456
fcallerd_type 0=OR, 1=AND 1
fcaller_domain / fcalled_domain SIP domain sip.example.com
fsipcallerip / fsipcalledip SIP IP address 192.168.1.1
fcallid SIP Call-ID abc123
fsipresponse SIP response code 503
fdurationgt / fdurationlt Duration (seconds) 10
fsensor_id Sensor ID 1
fcodec Codec numbers (comma-sep) 0,8
suppress_results 1=count only 1

Paging: page, start, limit

Sorting: sort=[{"property":"calldate","direction":"DESC"}]

⚠️ Warning: Use = (equals) for parameters, not : (colon). Example: fcallerd_type=1 (correct), not fcallerd_type:1 (wrong).

Wildcards

Use %25 (URL-encoded %) for SQL LIKE patterns:

# Caller starting with "00"
curl -G 'http://server/php/api.php?task=getVoipCalls&user=USER&password=PASSWORD' \
  --data-urlencode 'params={"startTime":"2024-01-01","caller":"%2500%25"}'

Codec Values

Value Codec Value Codec
0 PCMU (G.711 μ-law) 8 PCMA (G.711 A-law)
3 GSM 9 G.722
4 G.723 12 QCELP
18 G.729 97 iLBC
98 Speex 301-305 SILK variants
306-308 iSAC variants 1000 T.38 (Fax)

RTP Quality Filters

Parameter Description
fmosf1, fmosf2, fmosadapt MOS score filters
frtcp_maxjitter, frtcp_avgjitter RTCP jitter
frtcp_maxfr, frtcp_avgfr RTCP frame rate
floss1 - floss10 Packet loss distribution
f_d50, f_d70, f_d90, ... Delay distribution (50ms, 70ms, etc.)

Utility Endpoints

Direct PCAP Download

curl 'http://server/php/pcap.php?id=12345'           # Full PCAP
curl 'http://server/php/pcap.php?id=12345&disable_rtp=1'  # SIP only

CDR URL for Browser

http://server/admin.php?cdr_filter={fcallid:"abc123"}

SIP History

Requires session authentication first.

# JSON data
curl --cookie "PHPSESSID=..." 'http://server/php/pcap2text.php?action=brief_data&id=12345'

# HTML table
curl --cookie "PHPSESSID=..." 'http://server/php/pcap2text.php?action=brief&id=12345'

# MSC diagram
curl --cookie "PHPSESSID=..." 'http://server/php/pcap2text.php?action=getMSC&id=12345'

License Check

# Basic license check
curl 'http://server/php/apilicensecheck.php?task=licenseCheck'

# Concurrent calls limit check
curl 'http://server/php/apilicensecheck.php?task=licenseCallsLimitCheck'

Share CDR Configuration

Share Link Types

Use /php/model/utilities.php with task=shareCdr:

Type subType Description
Local Public self_protected_link Password-protected local link
Local Private self_login_link Requires GUI login
voipmonitor.org share_link Public via share.voipmonitor.org

Custom Branding

Product Name: Edit config/system_configuration.php:

define('BRAND_NAME', 'YourCompany');

Share Domain: Edit brand.php to use your domain instead of share.voipmonitor.org:

define('BRAND_SHARESITE', 'share.yourdomain.com');
define('BRAND_DOMAIN', 'yourdomain.com');

Custom Login (LDAP/SSO)

Custom authentication via scripts/custom_login.php. This applies to GUI login only, NOT to API authentication.

Basic Structure

<?php
function custom_login($user, $password) {
    // Authenticate against external system (LDAP, etc.)
    // ...

    return array(
        'username' => $user,
        'id' => $uniqueNumericId,  // REQUIRED: Must be unique per user!
        'is_admin' => false,
        'id_group' => 1,           // Optional: GUI group ID
        'enable_sensors' => array(1,2,3)  // Optional: restrict to sensors
    );
}
?>

⚠️ Warning: The id field MUST be unique per user. If multiple users return the same ID, they share ALL settings (timezone, dashboard, etc.). Use LDAP uidnumber or crc32($email) for uniqueness.

LDAP Example

See scripts/ldap_custom_login_example.php in GUI directory.

Requirements: php-ldap package installed.

Debug from CLI:

cd /var/www/html/scripts/
php custom_login.php

Enable debug by uncommenting the debug block at top of script.

Troubleshooting

Users share settings: Return unique id per user (see warning above).

LDAP users can't view CDRs: Ensure is_admin => false and correct id_group is returned.

Script being deleted: GUI antivirus deletes scripts with shell_exec(), exec(), etc.

Solutions:

  1. Disable antivirus in config/system_configuration.php:
define('DISABLE_ANTIVIRUS', true);
  1. Or move shell commands to external file outside web directory and require_once() it.

Azure AD / Microsoft SSO

For Microsoft Entra ID integration, see Microsoft_Sign_in_usage. For Google, see Google_Sign_in_usage.

Return Array Parameters

Full list of available return parameters for custom_login:

username, name, id, id_group, group_name, is_admin, email
enable_sensors - array of sensor IDs user can access
can_cdr, can_write_cdr, can_play_audio, can_download_audio
can_listen_active_call, can_pcap, can_upload_pcap
can_messages, can_view_content_message, can_graphs
can_activecalls, can_register, can_sip_msg, can_livesniffer
can_capture_rules, can_audit, can_alerts_edit, can_reports_edit
can_dashboard, can_ipacc, can_mtr, can_sensors_operations
can_network, can_edit_codebooks, hide_license_information
ip, number, domain, vlan (user restrictions)
custom_headers_cdr, custom_headers_message
blocked, blocked_reason, req_2fa

See Also


AI Summary for RAG

Summary: VoIPmonitor Web API reference with two main APIs: (1) HTTP API 2 at /php/api.php using user/password authentication for tasks like getVoipCalls, getVoiceRecording, getPCAP, listActiveCalls, getShareURL, handleActiveCall, reportSummary; (2) CDR HTTP API at /php/model/sql.php using session cookies with full GUI filter support. Critical: correct endpoint path is /php/api.php (not /api.php). reportSummary requires pre-configured reports in GUI. Custom login via scripts/custom_login.php enables LDAP/SSO - requires unique numeric ID per user to avoid shared settings. Antivirus may delete scripts with shell_exec - disable with DISABLE_ANTIVIRUS or move to external file. Branding: BRAND_NAME for product name, BRAND_SHARESITE/BRAND_DOMAIN in brand.php for custom share domain.

Keywords: web api, http api, api.php, sql.php, getVoipCalls, getVoiceRecording, getPCAP, getShareURL, listActiveCalls, reportSummary, handleActiveCall, CDR filter, custom login, LDAP, authentication, session, curl, JSON, branding, BRAND_NAME, BRAND_SHARESITE, antivirus, DISABLE_ANTIVIRUS, unique user id, uidnumber

Key Questions:

  • What is the correct VoIPmonitor API endpoint path?
  • How do I retrieve CDRs via the VoIPmonitor API?
  • How to download PCAP or voice recordings via API?
  • How to list active calls via API?
  • How to generate reports via API?
  • How to configure LDAP authentication for VoIPmonitor?
  • Why do LDAP users share settings? (need unique ID)
  • Why is custom_login.php being deleted? (antivirus)
  • How to customize VoIPmonitor branding?
  • What's the difference between api.php and sql.php?
  • How to use wildcards in API filter parameters?