API CallHistoryView $filter datetime parameters

mpedwatty

SMB User
Joined
Feb 24, 2025
Messages
5
Reaction score
1
Hi,

I am currently building an interface between our companies 3CX system and an internal colleague tracking system to display information, most of this is complete and working fine, including using the API

I am now trying to retrieve call history logs, as there is a lot of data, only new members are even returning a response before a timeout error, as we only need to get the start time, end time, call time, call answered and record id for the current day, I would like to filter by the SegmentStartTime, but I'm unsure how to format the query to work.

I am using
{{baseUrl}}/xapi/v1/CallHistoryView

I do get results when I filter by SrcDn, but for some users this does timeout due to the amount of data, so I'm looking for the correct formatting for a date query please
 
Use a ISO Zulu datetime format.
 
Sorry, its not the format of the datetime, its how the entire query should be formatted, I'm using Postman at the moment to test, but the URL param im using is
?$filter=SegmentStartTime ge '2025-02-24T00:00:00Z'
 
Not sure if you should use $filter or $where, but that query should work.
 
I have tried on $where, but that just gives a timeout

Using with $filter gives a 500 internal server error within a few seconds,
but using $filter for a newer user extension does return results fine, so it must be the correct param, unless datetimes use something else
 
Have you tried using the ReportCallLogData endpoint instead? We have that successfully working with filters and such.
 
Thank you for your assistance on this.

I have tried using that endpoint and get a 404 error, tried with pbx function call and params and empty
 
Thank you for your assistance on this.

I have tried using that endpoint and get a 404 error, tried with pbx function call and params and empty
DM me. I'll send you what we use.
 
We have exactly the same issue, could you please post a working GET URI if you got it to work? The whole URI, e.g.

https://<mysite>.3cx.eu:5001/xapi/v1/CallHistoryView?$filter=SegmentStartTime ge '2025-02-26T00:00:00Z'

I got it working filtering by SrcExtendedDisplayName, SrcDn, DstDn and I can get filter queries with eq, gt, contains for those fields, even multiple contains combined with or , but can't get anything working with date/time data.

As soon as I use SegmentStartTime in the filter, I get error (500) Internal Server Error

Please be kind and share the syntax so we can all learn, thanks!

I am currently trying this in Postman and PowerShell.

Here is the WORKING PowerShell code for API name/key access I'm working with at the moment.
(ignore the ugly code, it's still in early testing phase)

Code:
$mysite = 'yoursitenamehere'
$credentials = Get-Credential
$user = $credentials.UserName
$key = $credentials.GetNetworkCredential().Password
$postParams = @{client_id=$user;client_secret=$key;grant_type='client_credentials'}
$request = Invoke-WebRequest -Uri https://$mysite.3cx.eu:5001/connect/token -Method POST -Body $postParams
$content = $request.Content| ConvertFrom-Json
$token = $content.access_token
$headers = @{Authorization="Bearer $token"}

$HistoryURI = "https://$mysite.3cx.eu:5001/xapi/v1/CallHistoryView?"
$filter = '$filter='
$filtercontent = "contains(DstDn,'1423') or contains(DstExtendedDisplayName,'VMail (1432)') or contains(SrcDn,'1324') "
$FullURI = "$HistoryURI$filter$filtercontent"
Invoke-RestMethod -Method Get -Uri "$FullURI" -Headers $headers | Select-Object -ExpandProperty value | Sort-Object -Property SegmentStartTime -Descending | Select-Object -First 50 | Select SegmentStartTime, SegmentEndTime, SrcExtendedDisplayName, SrcDn, DstExtendedDisplayName, DstDn, CallTime, CallAnswered | ft

If you reuse the code, just change the "yoursitenamehere" to whatever you use, you may need to change the port as well if you use something else. Note this is for use with API key, not username/password, though you'll get pop-up prompt from powershell to enter username/pass, just enter key name and key secret instead :)

If I switch $filtercontent to anything related to SegmentStartTime it errors out, as if that field isn't supported by filter.
 
I havent been able to get the URL working with the datetime yet, I'm currently looking into using ReportCallLogData endpoint as suggested by @ConceptsWeb, hopefully I will have better look.

If I do find a solution I will defo message and post it here for people though
 
  • Like
Reactions: Evolute IT
Thanks for this! I've successfully exported a log containing almost 40k lines in under 15sec, so I could probably do 100k before timeout hits me. I used combination of filters mentioned above to trim the data since date/time is throwing errors. So I am sort of fine with this workaround (for now). But I'll eventually hit the needs of management looking for monthly data and such, so filtering by date will always be very welcome feature.

As for ReportCallLogData endpoint, I too was getting 404 but I found this other thread so I'll tinker with that as well when I get to it, thanks to you both!
 
  • Like
Reactions: Evolute IT
Hey all, I was looking at this again today, had some ok-ish ideas and such, but they're all workarounds for the initial problem.
I've noticed all fields that work with eq ge gt contains and other operators are ones that are STRING types, everything else throws 500 error.

So I started looking at the original issue, being - 504 timeout.

I've since found that on the server in nginx.conf ( C:\Program Files\3CX Phone System\Bin\nginx\conf\nginx.conf ) there is a block:

INI:
        location ~ ^(/xapi/v1/Report|/xapi/v1/ChatHistory|/xapi/v1/ChatMessages) {
            include "shared-headers.conf";
            include "mc.conf";
            proxy_read_timeout    1h;
        }

Note the 1h timeout, but also note this is for CHAT, not the CALL endpoints. So I'm thinking if I should maybe add the Call endpoint to this block, or make a similar one that contains longer timeout.

I haven't yet touched 3CX configs, and the VOIP admin is wary about the idea, so ... what say you? Should I try it?

EDIT: Browsing around there are other timeout settings I could also include if I make it a separate block, eg.
Code:
  proxy_connect_timeout       600;
  proxy_send_timeout          600;
  proxy_read_timeout          600;
  send_timeout                600;
 
Hey all, I was looking at this again today, had some ok-ish ideas and such, but they're all workarounds for the initial problem.
I've noticed all fields that work with eq ge gt contains and other operators are ones that are STRING types, everything else throws 500 error.

So I started looking at the original issue, being - 504 timeout.

I've since found that on the server in nginx.conf ( C:\Program Files\3CX Phone System\Bin\nginx\conf\nginx.conf ) there is a block:

INI:
        location ~ ^(/xapi/v1/Report|/xapi/v1/ChatHistory|/xapi/v1/ChatMessages) {
            include "shared-headers.conf";
            include "mc.conf";
            proxy_read_timeout    1h;
        }

Note the 1h timeout, but also note this is for CHAT, not the CALL endpoints. So I'm thinking if I should maybe add the Call endpoint to this block, or make a similar one that contains longer timeout.

I haven't yet touched 3CX configs, and the VOIP admin is wary about the idea, so ... what say you? Should I try it?

EDIT: Browsing around there are other timeout settings I could also include if I make it a separate block, eg.
Code:
  proxy_connect_timeout       600;
  proxy_send_timeout          600;
  proxy_read_timeout          600;
  send_timeout                600;
Don't touch the Nginx config.
 
So true, probably something would break with Nginx...

It also seems I won't be needing to do that change. I was SO stupid. Sure enough since I was testing and copy/pasting, I kept trying to use everything with same syntax, eg. $filtercontent = "DstDn eq '9195'" which was fine for strings, but I then used same, including single quotes, for integers, sure enough it threw 500 errors. Correct would be something like this:
$filtercontent = "SegmentId ge 907464"
Now when I can fetch and filter by SegmentId I can use that instead of time. It isn't perfect, as I don't know exact ID from which I would start, but that's easy enough to approximate or retry.

For example I just pulled about 10k CallHistory records by using something like this (obfuscated URL and headers for obvious reasons):

Invoke-RestMethod -Method Get -Uri https://My3cxWebSiteHereObviously.3cx.eu:5001/xapi/v1/CallHistoryView?$orderby=SegmentStartTime&$top=10000&$skip=0&$select=SegmentId,DstDn,SrcDn,DstParticipantId,DstDnType,SegmentStartTime&$count=true&$filter=SegmentId ge 900000 -Headers <Bearer Token Here!> | Select-Object -ExpandProperty value | Sort-Object -Property SegmentStartTime -Descending | ft

You can also get a total count of your query by adding ...&$count=true&... to your query (as shown above) so you can use skip and top to get just part of the dataset etc. If you use $skip=0 as above it will start from start. I left it there in example just so people have something to play with which is confirmed to work.

I can write more about this in detail, but I won't be at office next couple days, if it's urgent let me know and I'll try to get some moment to pull out my laptop and write a more proper guide and explanation.
 
I actually got date to work! It's a bit roundabout, as you need to specify year as separate filter then month as another, didn't try yet but I assume same can be applied to days and such. Anyway, here is the syntax:

Code:
Invoke-RestMethod -Method Get -Uri
https://Your3cxWebSite.3cx.eu:5001/xapi/v1/CallHistoryView?$orderby=SegmentStartTime&$top=100000&$skip=0&$select=SegmentId,SegmentStartTime,SrcExtendedDisplayName,DstExtendedDisplayName,CallTime,CallAnswered&$count=true&$filter=year(SegmentStartTime) ge 2025 and month(SegmentStartTime) eq 02 -Headers

Note the ending:
$filter=year(SegmentStartTime) ge 2025 and month(SegmentStartTime) eq 02

Fount this because while searching available operators I've discovered it's based on OData specification, found some Microsoft OData documentation, unfortunately it didn't cover date/time. So I kept searching, found that there were Odata V2/v3/v4 etc specs that handled that differently, no examples worked for any of the versions. Then I came upon this Reddit post and that worked!

Edit: Day works as well:
"year(SegmentStartTime) ge 2025 and month(SegmentStartTime) eq 02 and day(SegmentStartTime) eq 25"
 
  • Like
Reactions: Evolute IT
OK, some more info related to date/time, and a full working PowerShell example at the end of this post!

First of all I found OData specs, in particular the filter section, here:
https://docs.oasis-open.org/odata/o...t1-protocol-complete.html#_The_$filter_System

So here is few more filters that could be helpful:
Code:
$filtercontent = "DstDn ne 'EndCall' and SrcDn ge '1234'"
$filtercontent = "DstDn eq 'EndCall' and SrcDn lt '1324'"
$filtercontent = "SrcId in (9123149,8113248,7112347,6132146)"
$filtercontent = "SegmentId ge 123456"
$filtercontent = "year(SegmentStartTime) eq 2025 and month(SegmentStartTime) eq 02 and day(SegmentStartTime) eq 25 and hour(SegmentStartTime) eq 11 and minute(SegmentStartTime) eq 09"
$filtercontent = "date(SegmentStartTime) eq 2025-02-24"
$filtercontent = "date(SegmentStartTime) ge 2025-02-01 and date(SegmentStartTime) le 2025-02-28"
$filtercontent = "date(SegmentStartTime) ge 2025-02-01 and date(SegmentStartTime) le 2025-02-28 and CallAnswered eq false"

I append $filtercontent after $filter in the URI, just to be clear, you can see it in full example below, so you can mix and match as you need.

As you can see, for this discussion, most helpful is the date(SegmentStartTime) eq 2025-02-24 that completely solves the issue @mpedwatty had. Using date(SegmentStartTime) you can filter exact date, or (using gt/ge/lt/le operators) get the date range (eg month, year, week), which is perfect for reports and analytics of any kind.

I'd also like to confirm that I can now filter by any field in the CallHistoryView, except I haven't yet figured out CallTime, but I don't really see point in filtering by the length of the call, as long as I can fetch it that's enough. Other data types (strings, integers, boolean) work fine and you can see those in example filter code above.

Now, below is a piece of PowerShell code that works for selectig one month (February 2025 in this case) by using API key (NOT! username/passsword!). I've included instructions how to get API key (Client ID and secret/key) in case you don't know how.

Code is functional by simply copy/pasting to PowerShell, just make sure to change $website to your actual domain (and port if different 5001). When asked for credentials just use those you've created in API Integrations section of your 3CX Web UI.

One more note, make sure you don't have firewall or console access limitations set in a way that your connection would be blocked in the first place. If you can access standard web console URL from the device you're running this code from, then it should work just fine. Double checking wouldn't hurt!

Finally, the working code (MAKE SURE TO CHANGE $website TO YOUR WEB CONSOLE URL!!)

Code:
### FOR 3CX COMMUNITY FORUM POST
### TESTING DATE/TIME FILTERING WITH CALL HISTORY VIEW ENDPOINT

# CREATING 3CX API INTEGRATION CLIENT ID & KEY/SECRET
# 1) Login with system owner account to your 3CX Web UI / web console
# 2) Click Admin (bottom left corner)
# 3) Under: Integrations -> API -> click "+ Add"
# 4) Enter "Client ID" e.g. "test", set Department to "DEFAULT" and Role to "System Owner"
# 5) Save, you will get a new pop-up window, click the Copy icon and save your API key/secret somewhere safe, and confirm with OK
# 6) Tripple check that your API integration was saved as system OWNER because system ADMIN will NOT WORK FOR CALL HISTORY VIEW!

# Login with your Client ID e.g. "test" as "User name" and the API key/secret that was generated for you as "Password" in the "Windows PowerShell credential request" pop-up that you'll get after "Get-Credential" is executed

# Just making sure all variables are blank before starting
$website = ''
$port = ''
$URL = ''
$credentials = ''
$user = ''
$key = ''
$postParams = ''
$request = ''
$content = ''
$token = ''
$headers = ''
$HistoryURI = ''
$filter = ''
$filtercontent = ''
$FullURI = ''
$callhistory = ''

# Change this variable to your own correct website domain and port
$website = 'YourSubdomainHere.3cx.eu'
$port = '5001'
$URL = 'https://'+$website+':'+$port

$credentials = Get-Credential
$user = $credentials.UserName
$key = $credentials.GetNetworkCredential().Password
$postParams = @{client_id=$user;client_secret=$key;grant_type='client_credentials'}
$request = Invoke-WebRequest -Uri $URL/connect/token -Method POST -Body $postParams
$content = $request.Content| ConvertFrom-Json
$token = $content.access_token
$token
$headers = @{Authorization="Bearer $token"}

# This will fetch top 100.000 records from call history for 2024-02, will print to console first and last 30 entries as preview,
#    and will then proceed to export whole dataset as CSV and XLSX files to local directory
#    (PowerShell module "ImportExcel" required for XLSX export, for installation see link: https://github.com/dfinke/ImportExcel )

$HistoryURI = $URL+'/xapi/v1/CallHistoryView?'
$filter = '$orderby=SegmentStartTime asc&$top=100000&$skip=0&$select=SegmentId,SegmentStartTime,SrcExtendedDisplayName,DstExtendedDisplayName,CallTime,CallAnswered&$count=true&$filter='
$filtercontent = "date(SegmentStartTime) ge 2025-02-01 and date(SegmentStartTime) le 2025-02-28"
$FullURI = "$HistoryURI$filter$filtercontent"
$callhistory = Invoke-RestMethod -Method Get -Uri "$FullURI" -Headers $headers
$callhistory | Select-Object -ExpandProperty value | Select-Object -First 30 | ft
$callhistory | Select-Object -ExpandProperty value | Select-Object -Last 30 | ft
$callhistory | Select-Object -ExpandProperty value | Export-Csv -Path CallHistoryView-2025-02.csv
Import-Module ImportExcel
$callhistory | Select-Object -ExpandProperty value | Export-Excel -Path CallHistoryView-2025-02.xlsx

# To see total count simply call:
$callhistory

# If count is lower than 100k, and matches number of rows in your CSV / XLSX, then you have it all exported successfully

Code isn't exactly how someone would actually write this properly, it is meant for testing and readability and easier understanding what is calling what and what is doing what. If more comments are needed please let me know and I'll fill in the blanks.

If you read this and found it useful throw a like below, I'm always interested to see if I helped anyone :-) Cheers!
 
I've added my PowerShell scripts to github here, so they are available to anyone in need of using or researching this XAPI:

https://github.com/luxzg/3CX-XAPI_examples

Hope it helps! And thanks again to everyone in this thread that helped with their own input!
 
Hey all, probably my final post on this thread, just wanted to say I've also written a huge readme on the GitHub that explains a lot of stuff I've encountered while doing all this... Hopefully it helps others as a reference related to this XAPI in general. You can just go to GitHub linked above, or directly to Readme file here:

https://github.com/luxzg/3CX-XAPI_examples/blob/main/README.md
 
  • Like
Reactions: Evolute IT

Latest Posts

Members Online Now

Forum statistics

Threads
111,832
Messages
589,278
Members
164,662
Latest member
DejanMDS