3CX support
| 3CX support is experimental. |
Running it
uniloader 3cx [flags]
uniloader 3cx connects to the 3CX PBX, follows its calls and the state of its agents,
and writes what it sees as a queue_log file. It only writes the file: as with FreeSwitch,
the file must then be uploaded to QueueMetrics by a separate instance of uniloader upload,
reading the same file.
Before starting: an API application on 3CX
Uniloader reads the PBX through the 3CX API, logging in as an API application. Create one in the 3CX admin panel, under Admin → Integrations → API, and take note of its client ID and client secret. The application must:
-
have access to the Call Control API, to receive call events;
-
have access to the 3CX Configuration API (XAPI), with a role that can read queues, ring groups and users;
-
see every queue and every agent to be tracked: calls on extensions or queues the application cannot see never reach Uniloader.
| If queues or agents cannot be read even though the panel shows the right role, press Save on the API application once, without changing anything: this refreshes the permissions of the credentials it hands out. |
Once the application is created, check that Uniloader can use it, before running anything else, with the sub-command test 3cx: it logs in with those same credentials, prints the version of the PBX and the roles that were granted, lists the extensions and the queues the application can see, and makes sure that call events can be received.
That list is the one to check against the queues you mean to track: an extension or a queue that is not selected for the API application produces no events at all, so nothing about it is ever written to the queue_log.
Select the queues AND all of their agents
Selecting only some of the DNs involved in a call does not give you a smaller version of the truth - it gives you a partial and misleading one. What is written depends on which side of the call Uniloader can see:
| What happens | What is written to the queue_log |
|---|---|
A queue and all of its agents are selected |
Everything: the call, the rings, the answer, how it ended |
A call on a queue that is not selected, answered by an agent that is |
The call, on that queue - but see the two limits below |
The same call, with nobody answering |
Nothing at all |
The same call, answered by an agent that is not selected |
Nothing at all |
A call of an extension that is not selected, to the outside or to another extension |
Nothing at all |
The two limits, when a call is seen only through the agents that answer it:
-
the waiting time is too short. The call is dated at the first ring Uniloader can see, not at the moment it entered the queue, which is invisible: the longer a call waited, the more the wait is understated.
-
the rings towards agents that are not selected are missing. Those towards selected agents are written normally, so the same call may show two rings when it really had five.
The queues themselves are read from the Configuration API, which sees them all: a queue that is not selected for call events is still created in QueueMetrics by pbxinfo 3cx, with all of its agents, and their logons and pauses are still written. It would therefore look like a perfectly normal queue whose reports are quietly wrong.
| If a queue matters to you, select the queue and every one of its agents, then check the list printed by test 3cx. |
uniloader test 3cx --url https://pbx.example.com --client-id uniloader --client-secret ab1234
Ring groups
In QueueMetrics a ring group is simply a queue, named after its number (e.g. 850): calls,
rings, answers, abandons and timeouts are reported exactly as for a queue, and everything said
above about selecting a queue and all of its agents applies to ring groups too.
The one difference is that a ring group has no login: its members do not log on to it, they are simply rung. So each agent is made a member of the ring group only for the length of the call that rings it - it joins at its first ring and leaves when the call ends - and does not appear in the ring group in between calls. If two calls ring the same agent at the same time, the first one to end removes the agent while the other is still ringing it.
pbxinfo 3cx creates a queue in QueueMetrics for every ring group, with its members as known agents, just as it does for queues.
Required Flags
| Flag | Type | Description |
|---|---|---|
|
string |
Base URL of the 3CX PBX (e.g. |
|
string |
Client ID of the 3CX API application |
|
string |
Client secret of the 3CX API application. You could also set it in the |
The client secret can be provided via --client-secret or the PBX3CX_PWD environment variable
(preferred, to avoid exposing credentials in shell history or process listings).
Optional Flags
| Flag | Type | Default | Description |
|---|---|---|---|
|
string |
|
The queue_log file to write. It is created if missing, and appended to otherwise. |
|
int |
10 |
How often, in seconds, the state of queues and agents is read; at least 1 |
|
int |
2 |
How many call events are downloaded at the same time; at least 1 |
|
int |
0 |
The hour of the day (0-23, local time of the host running Uniloader) after which the state of all agents is written again to the queue_log. The default, 0, is midnight. |
|
string |
— |
A debug file where every event and every poll read from 3CX is appended, in |
|
string |
— |
The PID file to write. If already present, Uniloader won’t start. It is removed when Uniloader stops. |
What happens when it runs
-
At start, Uniloader logs in and reads the state of queues and agents. If either fails, it stops at once with an error.
-
The agents logged in at that moment are written to the queue_log first, as
ADDMEMBERrows. Then Uniloader follows the call events, and reads again queues and agents every--agent-pollseconds, writing logons, logoffs and pauses as they change. -
Once a day, at the first reading of queues and agents after the hour set by
--repeat-agents-hr(by default, soon after midnight), the state of every agent is written again: anADDMEMBERrow for each queue the agent is logged on to, and aPAUSEALLrow for each agent on pause. This way each day of queue_log is complete by itself, and QueueMetrics knows who is logged on and paused even when it does not read the days before. If that reading fails, the next one that succeeds does it. -
The access token is renewed automatically every 30 minutes. If it cannot be renewed before it expires, Uniloader logs the error and stops: nothing more could be read from the PBX.
-
If the connection to the event stream drops, Uniloader logs it and reconnects at once, then waits a little longer after each failed attempt, up to 10 seconds. Events sent by the PBX while disconnected are lost, and a message in the log says so.
-
A call event that cannot be downloaded, even after a retry, is dropped, and an
ERRORmessage in the log says so.
The file --events is optional, but we suggest creating it while 3CX support is
experimental: it records everything Uniloader received from the PBX, so that anomalies in
the queue_log can be tracked down.
Leaving a single queue from the 3CX panel is not seen
An agent can leave one queue from the 3CX web client panel and stay in the others. That change does not reach QueueMetrics: the agent goes on looking available in a queue it has left, and the time it spent out of that queue is counted as time on duty.
The PBX behaves correctly - it stops sending that agent calls from that queue - so what you get is not wrong routing, but wrong statistics: an agent apparently available that never answers, and availability figures that are too high for the queue.
The reason is what the API tells us: queue membership is the one set by the administrator, and the logged-in flag is one for the whole user - "in every queue" or "in none" - so the PBX has no way of telling us about a single queue. What Uniloader does see, and writes correctly, is:
-
logging out of all queues at once, from the client or the phone;
-
changes of presence profile (
Available,Do not disturb, a custom one), written as pauses with the profile as their reason; -
queue membership changed by the administrator.
| Until this is addressed, tell your agents to log out of the queues altogether rather than leaving them one by one, and to use the presence profiles for pauses. Those are the actions QueueMetrics sees. |
Configuring QueueMetrics
The queues and the agents that Uniloader finds on the PBX can be read, and sent to
QueueMetrics as a ready-made configuration, with pbxinfo 3cx. It takes the
same credentials as uniloader 3cx, so that the queue_log written here refers to agents
and queues that QueueMetrics already knows:
uniloader pbxinfo 3cx --url https://pbx.example.com --client-id uniloader --client-secret ab1234
Minimal Example
export PBX3CX_PWD="the-client-secret" uniloader 3cx \ --url "https://pbx.example.com" \ --client-id "uniloader" \ --queuelog "/var/log/uniloader/3cx_queue_log"
Full Example
export PBX3CX_PWD="the-client-secret" uniloader 3cx \ --url "https://pbx.example.com" \ --client-id "uniloader" \ --queuelog "/var/log/uniloader/3cx_queue_log" \ --agent-poll 15 \ --event-downloaders 4 \ --repeat-agents-hr 3 \ --events "/var/log/uniloader/3cx_events.3cx_log" \ --pid "/var/run/uniloader-3cx.pid"