API reference

This chapter documents the different WombatDialer APIs available. As a reference point, we have a public repository with some examples and integration code that leverages APIs at https://github.com/Loway/OpenWombatDialerAddOns where you can find scripts and examples. We welcome contributions!

Controlling Asterisk from WD

When a call is originated on Asterisk, a set of channel variables are set:

  • WOMBAT_HOPPER_ID is a unique identifier of the call being processed (on old versions, it used to appear as WOMBAT_ID)

  • WOMBAT_RETRY_NO is the number of the current recall

  • WOMBAT_DIALING_NUMBER is the main number being called (that may be different from the actual one dialed when using multinumbers)

  • WOMBAT_DIALING_LIST is the name of the list that this number comes from

  • all input attributes are passed to the call

This way, all attributes you set for the call are immediately available within the Asterisk dialplan.

For example, if you define input attributes called HH and MM in WD, you can use them in the dialplan to synthesize an audio message as in the following example:

exten => s,n(start),agi(googletts.agi,"Your appointment with the dentist
                is for tomorrow at ${HH} ${MM}")

If you do a DumpChan() of the channel that Wombat triggered, you will see something similar to the dump below:

Dumping Info For Channel: Local/5551234@wdtk-00000006;1:
========================================================
Info:
Name=               Local/5551234@wdtk-00000006;1
Type=               Local
UniqueID=           1700210157.12
LinkedID=           1700210157.12
...

Variables:
WOMBAT_HOPPER_ID=1555774267
WOMBAT_DIALING_LIST=CUSTOMERS2307A
WOMBAT_RETRY_NO=0
WOMBAT_DIALING_NUMBER=5551234
NAME=John
SURNAME=Doe
HH=10
MM=30
MULTINUM1=5553456
========================================================

Controlling WD from Asterisk

WD lets you pass information about a call that is currently processed in Asterisk in the form of User Events. This lets you set the completion code for the call and any additional parameters, e.g. IVR responses, right from the Asterisk dialplan.

UserEvents can only be set on calls that are connected; if sent before the call is up, they will be ignored.

Format of User Events

User events are triggered through the dialplan through the UserEvent Asterisk application.

They are used to:

  • Set the completion code for a given call

  • Store additional outbound attributes on the call in WD

Triggering a user event

You can easily trigger User Events by calling the Asterisk dialplan like:

exten => s,n,UserEvent(name,Uniqueid:${UNIQUEID},P0:parm1)

or

exten => s,n,UserEvent(name,Uniqueid:${UNIQUEID},P0:parm1|P1:parm1)

In case you want to pass two parameters.

The event name is not case sensitive.

The Uniqueid field must match the ID that is used for the main leg of the call; so in case you are dialing in reverse mode, you should use:

exten => s,n,UserEvent(name,Uniqueid:${CHANNEL(LINKEDID)},P0:parm1)

To make sure you are using the right identifier.

You can see the "canonical" Uniqueid on a WombatDialer call by going to the "Call Log Record" of your call and checking under "Technical details". You can also see it on live Asterisk calls by using the CLI command core show channel …​ when the call is up.

Setting call status codes

In order to set the extended status (completion code) of a call, you should trigger an UserEvent like:

exten => s,n,UserEvent(CALLSTATUS,Uniqueid:${UNIQUEID},V:XXX)

This sets the completion code for the current call to XXX. If multiple completion codes are received for the same call, the last one wins. Completion codes are then used by WD’s Reschedule Rules in order to decide what to do with the call.

Output call variables

You can set an output attribute for the current call by running:

exten => s,n,UserEvent(ATTRIBUTE,Uniqueid:${UNIQUEID},name:value)

This will create an attribute called "name" with value "value". If the same attribute already exists, it is replaced.

As an alternative, you can append values to an existing attribute, like:

exten => s,n,UserEvent(ATTR_APPEND,Uniqueid:${UNIQUEID},name:value)

This will append the value "value" to the current value of attribute "name"; if the attribute is not present, it will be created with value "value". This makes it easy to to track the traversal of IVR trees.

You may also want to clear all output attributes (but of course not input ones) of a call by issuing:

exten => s,n,UserEvent(ATTR_CLEAR,Uniqueid:${UNIQUEID})

Please note that:

  • Attributes are per number; so if you call the same number multiple times, its attributes will be the last ones used.

  • All attributes are text strings

  • A number may have an unlimited number of attributes

It is possible to set status and attributes from the HTTP interface as well.

Controlling WD over HTTP

WD is built so that it can be controlled externally by an external script or dial-plan function. The protocol is very simple and - by default - not authenticated (but see below), so you must make sure that it is not reachable from unsafe parties. See Installing Wombat.

In the following examples, we assume that WD is running at address http://127.0.0.1:8080/wombat

It is important to notice that the '/'' before the question mark in the following examples is mandatory - if not present you will get an HTTP error 404.

Though the examples below display HTTP GET requests, the APIs work as well with HTTP POST calls.

If you use Tomcat 8+ as a servlet container then it will return an error 400 if you do not accurately URL-encode all characters according to RFC 7230 and RFC 3986. This often happens when passing a set of elements that are separated by the pipe symbol - so for example a|b will not work, but a%7Cb will. For readability’s sake, the examples below still show the pipe character, and it’s up to you encoding your inputs correctly.

Optional authentication

While it is not so by default, you can add an optional layer of security that will prevent access to basic API calls unless you hold one of the approved tokens. These tokens are specified system-wide through a JVM configuration property (see Additional environment configuration), and you can set one or more of them by separating them with a comma, as shown below:

wd.auth.tokens=3db6e6b81bfe8a,e51979ce575e949dc

These tokens are case-sensitive, and we suggest using random text sequences, as you best see fit in terms of length.

When the JVM property is set, all API calls below require an extra parameter auth_token_id that will be checked to see if it matches any of the tokens set. If none of them matches, then the request is denied.

For simplicity’s sake, all examples in this chapter are displayed without the extra auth parameter; but if you have a call like:

  http://127.0.0.1:8080/wombat/api/calls/?op=addcall&campaign=ABC&number=45678

And you enabled auth tokens, you need to add your token to the request, like:

  http://127.0.0.1:8080/wombat/api/calls/?op=addcall&campaign=ABC&number=45678&auth_token_id=3db6e6b81bfe8a

Engine control

The dialer can be started and stopped by the API, and its current state can be retrieved.

Retrieving current engine state
   http://127.0.0.1:8080/wombat/api/engine/?op=STATE

Will produce a JSON output like:

{
  "state" : "READY",
  "uptimeMs" : 5809,
  "msgSent" : 23,
  "msgReceived" : 108,
  "maxLicensedChannels" : 20
}

This shows:

  • The current state.

  • The current up-time for the dialer

  • The number of messages sent and received

  • The maximum number of licensed channels

Valid states are:

  • READY: Wombat is up and running.

  • DOWN: The dialer is currently switched off.

  • TERMINATION_REQD: The dialer was up and a message was queued to ask it to shut down. This may take a few seconds; you can check its state by sending a new STATE call.

  • TIMEOUT_NO_REPLY: The dialer did not answer within 10 seconds; it is either extremely overloaded, or it was shut down in the meantime.

Uptime, number of messages and licensing information will be zeroed out if the state is not READY.
Start and stop the engine

To start the dialer’s engine you call:

   http://127.0.0.1:8080/wombat/api/engine/?op=START

This call returns the current state. If you call this on an engine that is already started, nothing happens.

To stop the dialer, you call:

   http://127.0.0.1:8080/wombat/api/engine/?op=STOP

The reply is always a message of type TERMINATION_REQD, and you will have to check the state again in a few seconds. Note that this is a graceful termination; the dialer is requested to stop, so it will do it once the current set of messages is processed.

Once the engine is off, any pending call will remain pending, and it will not be tracked any longer, no matter what happens on the PBX. So, before terminating the engine, you should make sure that all runs are paused or terminated, and that no calls are ongoing. See below for the relevant APIs.

Calls

Add a call to a campaign

This methods lets you add a call to a campaign that is either running or idling.

  http://127.0.0.1:8080/wombat/api/calls/?op=addcall&campaign=ABC&number=45678

You can queue calls for the future by adding a reschedule attribute, either as an offset in seconds (positive integer) or an absolute date having the format YYYY-MM-DD.HH:MM:SS.

  schedule=15
  schedule=2012-03-18.00:00:00

You can also set multiple attributes, as in:

  attrs=ID:2301,PERS:John_Doe

If you add the same number multiple times, then it will be dialed multiple times.

Current versions of WombatDialer by default apply backpressure, that is, will not just enqueue a number for being added but will expect a confirmation that the operation was performed. As the number is actually handled by the main WombatDialer process, this means that it is scheduled together with the rest of current activities, and therefore the call might require tens or hundreds of milliseconds to be completed. On the other hand, this prevents you from uploading numbers faster than WD can handle them.

If you are sending numbers from a time-sensitive process (e.g. the Asterisk dial-plan) you may want to add the parameter fireforget=1 so that the call will return immediately. This was the default behavior up to WD 20.04. The downside is that if you push hundreds or thousands of numbers at once, they might require a lot of time to be fully processed, and this risks disrupting current calls or making the dialer unresponsive.

If the campaign is idling, it will be "woken up" automatically when new numbers are added.

It is important to understand that all calls added this way are technically reschedules, even if no schedule attribute is set. Still, the first attempt will be numbered '0', in order to keep a consistent approach when using multi-numbers.

This method is meant to drip-feed numbers to an existing run, as needed. If you need to upload hundreds or thousands of numbers at once, what you want to do is to upload a list of numbers at once and then add it to a campaign - this can be achieved though the Configuration API and/or the addToList method and has better performance.
Set extStatus and attributes on a live call

While a call is being dialed, it is possible to set a user event externally in a manner that exactly matches normal UserEvents. The important difference is that you do not need the UniqueID but the WombatID.

Set a call’s extStatus to XY:

  http://127.0.0.1:8080/wombat/api/calls/?op=extstatus&wombatid=123456&status=XY

Create or set attribute AB to value CD:

  http://127.0.0.1:8080/wombat/api/calls/?op=attr&wombatid=123456&attr=AB&val=CD

Append string EF to the current value of attribute AB:

  http://127.0.0.1:8080/wombat/api/calls/?op=attrAppend&wombatid=123456&attr=AB&val=EF

Remove all outbound attributes for this call:

  http://127.0.0.1:8080/wombat/api/calls/?op=attrClear&wombatid=123456

This API works as well for closed calls; so you can set extStatus and variables on calls that are already terminated. The most important limitation when doing so is that any Reschedule or Disposition Rules that would have been triggered by if the call had had the correct extStatus on termination are NOT triggered.

Campaigns and runs

Start a new run with an existing campaign

This call starts a new run an existing campaign; if the campaign is not startable (e.g. because it is already running) the call is ignored.

  http://127.0.0.1:8080/wombat/api/campaigns/?op=start&campaign=ABC
Pause a running campaign

This call pauses a campaign that is running or idling; if no run is found in a suitable state the call is ignored.

  http://127.0.0.1:8080/wombat/api/campaigns/?op=pause&campaign=ABC
Unpause a running campaign

This call unpauses a run that is currently paused, when unpaused it may start dialing or keep on idling or start waiting for a correct time to place calls.

  http://127.0.0.1:8080/wombat/api/campaigns/?op=unpause&campaign=ABC
Remove a run

This call removes an existing run; the run is supposed to be paused and you should either wait an appropriate amount of time or you should make sure no calls are active (if not, they will be forcibly closed in Wombat and will end up in state ERROR, while they will not be tracked anymore if they are active on the PBX).

  http://127.0.0.1:8080/wombat/api/campaigns/?op=remove&campaign=ABC
Reload a campaign

This call asks for a campaign to be reloaded, i.e. its definition to be read from the one that is currently saved in the database and not the one that was used when the campaign started.

This lets you pick up any changes you made to the campaign configuration on a running campaign.

  http://127.0.0.1:8080/wombat/api/campaigns/?op=reload&campaign=ABC

Reload does not pick up an updated definition for Asterisk servers, Trunks and EPs, as these objects are possibly shared across all campaigns. Still, it is possible to "change" an existing object, say an EP, by the following rules:

  • Create a new EP through the API

  • Edit a campaign; remove the old EP and add the one you just created

  • Reload the campaign

At this point, the new EP will start being used, while calls made on the old EP will still run until they terminate.

Clone a campaign
  http://127.0.0.1:8080/wombat/api/campaigns/?op=clone&campaign=xx&newcampaign=yy

This will clone existing campaign named ABC to a new one named DEF. It will clone only the campaign definition, so no activity statistics - just like if you were to create a new one from the GUI. After cloning, you’ll have to start the campaign run.

Add an existing list to a campaign

This call lets you add an existing list to an existing campaign (usually on a cloned one). It will not work on running campaigns as they will not pick-up the new configuration until restarted.

  http://127.0.0.1:8080/wombat/api/campaigns/?op=addList&campaign=QMS&list=ZEB
Add an existing list to a campaign as a black-list

This call lets you add an existing list as a black-list to an existing campaign (usually on a cloned one). It will not work on running campaigns as they will not pick-up the new configuration until restarted.

  http://127.0.0.1:8080/wombat/api/campaigns/?op=addBlackList&campaign=QMS&list=ZEB
Notify the state for an API-driven end-point
This feature is experimental and subject to possible changes.

This call lets you feed information to WD for an API-driven end-point.

  http://127.0.0.1:8080/wombat/api/endpoint/?op=update&queue=z1&n_idle=20

Parameters:

  • queue is the name of the EndPoint - it must be of type API, otherwise it will not be updated. On the other hand, multiple EPs of type API can share the same queue and will be updated at once.

  • idle, ringing, talking, paused: a comma-separated list of agents that are in each state. If no parameter passed, or left empty, each set of agents is considered to be empty.

  • calls: a list of calls that are present on the queue waiting to be connected. Calls that are already connected should not appear here (but their agents should appear under talking). You can use any Ids you like.

  • n_idle, n_ringing, n_talking, n_paused, n_calls: when the exact agent/call codes are not important (e.g. for direct dialling) you can use one of these parameters to have a list of entries created on the fly.

Agent codes should be "real" channel definitions for reverse or reverse-preview modes, at least for idle agents. When called on the Asterisk server, they should be able to ring the relevant phone on your destination system.

In all other cases (and for call lists), it is up to you whether you want to specify the exact agent and call codes, or you just want to update the system by sending in an aggregate. Internally, the aggregate will be expanded into a pro-forma list of items, for example n_idle=2 will expand to idle=idle/1,idle/2.

This call is synchronous, it will return only after the update has been picked up by the dialer, as to help enforce backpressure.

This service should be called every time there is an update, at least with the list of free agents. It is okay to batch multiple calls together if they happen within 100-300ms from each other. You can experiment with lower update rates (e.g. every second, or every few seconds) but the update rate should be significantly shorter than the average call set-up time.

When sending messages very quickly, do monitor that incoming messages are processed as they come - you don’t want to build up a backlog. See Monitoring a running instance.
Query current calls and recalls for runs

This call lets you query the state of ready and deferred calls for a given run or set of runs. Calls scheduled for the future are split into "reschedulingSoon", that is calls that are either immediately reschedulable or will become so within a short time limit (by default 5 minutes) and "reschedulingLater". The limit on which they are to be separated can be set by the user.

  http://127.0.0.1:8080/wombat/api/recallinfo/

The method accepts the following parameters:

  • 'campaign' is a substring to be searched in a campaign’s name. If missing, all runs will be shown.

  • 'late_reschedule' is the time limit - in seconds - that separates "soon" to be rescheduled calls from "late" reschedules. Default is 300.

The results shows all calls ready to be run (as they are in the hopper) and all live calls, split by status; plus all recalls scheduled for the run.

The output looks like the following one:

{
  "runs" : [ {
    "campaignName" : "ABC-3.15-fixed",
    "runName" : "15.03.25 16:18:36",
    "runId" : 123,
    "callsInHopper" : 10,
    "callsByState" : {
      "IN_HOPPER" : 10
    },
    "reschedules" : 0,
    "reschedulingSoon" : 0,
    "reschedulingLater" : 0
  }, {
    "campaignName" : "ABC Mexico",
    "runName" : "15.04.02 14:39:49",
    "runId" : 124,
    "callsInHopper" : 134,
    "callsByState" : {
      "IN_HOPPER" : 134
    },
    "reschedules" : 257,
    "reschedulingSoon" : 254,
    "reschedulingLater" : 3
  } ],
  "reschedule_late_limit_sec" : 300
}
Add (or restart) list to a running campaign

It is possible to add a list that the campaign was not originally started with to a running campaign. Lists are processed in sequence, so it will generally be read only after the original lists are fully scanned.

  http://127.0.0.1:8080/wombat/api/runlists/?op=start&campaign=Test&list=L1

If the run already has that list and the list is paused, then it is unpaused.

If you are looking at the current lists from the GUI, then you will have to refresh them to see the changes you made.

Pause list in a running campaign

In order to pause a list on a running campaign, you can use the following call.

  http://127.0.0.1:8080/wombat/api/runlists/?op=pause&campaign=Test&list=L1

If the list does not exist on your run, the service completes successfully but nothing happens.

In order to unpause a paused list, you call the 'start' operation again.

Please note that pausing a list stops Wombat from fetching further numbers from the paused list, but does not prevent Wombat to call numbers it has already fetched that are ready to be called. Using a smaller "batch size" prevents Wombat to fetch too many numbers in advance, and so might be of interest if you need to pause/unpause lists often (at the price of a higher database load).

A campaign that has paused lists will not terminate naturally, but will remain active until either you unpause those lists or you forcibly remove the campaign.

Show lists on a running campaign

You can query the association of lists to a specific running campaign.

  http://127.0.0.1:8080/wombat/api/runlists/?op=list&campaign=Test

The output shows all lists, specifying whether they are currently paused and whether they are already finished.

[ {
  "listId" : 2,
  "listName" : "TestList2",
  "highWater" : 131,
  "isRunning" : false,
  "isFinished" : false
}, {
  "listId" : 3,
  "listName" : "L1",
  "highWater" : 1203,
  "isRunning" : true,
  "isFinished" : true
} ]
Set boosting on a running campaign

You can set the boost factor on a running campaign:

http://127.0.0.1:8080/WombatDialer/api/campaigns/?op=boost&campaign=TestCampaign&factor=78

The boost factor is expressed as an integer percentage and must be between zero and 500 (5x).

Call lists

Add numbers to a list

This call adds the numbers passed in 'numbers' to the call list ZEB_2 - if it does not exist, it will create it. Numbers are a pipe-separated list of numbers and optional attributes.

  http://127.0.0.1:8080/wombat/api/lists/?op=addToList
         &list=ZEB_2&numbers=1234,AA:bb,CC:dd|5678,XX:yy

If you set the parameter 'dedupe=1' then numbers will be checked for existence on this this list before being added. This requires a significant database load, so use with caution on a running system where lists are over 10,000 elements each. Numbers are considered to be existent if they are present on the list no matter what their attributes might be.

It is very common to use HTTP POST to add numbers, so that you can upload a large set at once. The maximum request size depends on your servlet container, but is usually in the order of 1-2 megabytes.

Cancel numbers from a blacklist

This call lets you cancel a number from being blacklisted on a specific list:

    http://127.0.0.1:8080/wombat/api/calls/
      ?op=cancelBlacklist
      &list=DNC
      &numbers=5551000
      &reason=Incall1

This means that if there is number 5551234 on the list DNC that can be actively blacklist, mark it so that it is not used for black-listing, and write the reason for de-blacklisting it as "Incall1" (it’s just a string where you may store anything that makes sense to you). If the number is present multiple times, then all of them will have their attributes BLACKLISTED_CANCEL set to "Incall1 @2023-11-10.18:31:32".

If you specify no reason, then the current date will be used to set BLACKLISTED_CANCEL.

You can cancel blacklisting of one or more numbers at once - if you wish to cancel multiple numbers, they must be separated by a pipe symbol, and it must be properly URL-encoded.

The webservice returns a synchronous reply like OK: Numbers updated: 6 (N Lists 1 - Numbers found: 9) - that means that the list you asked was found, that the number was present 9 times, bus at three of them were already cancelled or expired, changes were made on just 6 entries.

Stow away: rename and hide lists in one go

Sometimes it is useful to work with lists that always have the same names, and to remove them when they are not used. As lists cannot be deleted from a Wombat system, it is useful to "stow away" them by renaming them and hiding them in one go.

  http://127.0.0.1:8080/wombat/api/lists/?op=stowAway&list=LEADS&as=LEADS_170112

This call renames your current list LEADS to LEADS_170112 and hides it; at this point you can simply create a new list called LEADS and use it.

You will have to manually remove the list from any campaigns that might be using it; otherwise they will keep using the old (hidden) list.

Preview dialing

You can control Preview Reverse dialing through the API. A somewhat simpler alternative may be using the Preview page.

Reserve a call

In order to reserve a call, you should call WombatDialer by issuing a request like the following:

http://127.0.0.1:8080/wombat/api/reserve/?op=reserve&agent=SIP/302

It is of paramount importance that the agent parameter matches an agent code as present in Asterisk.

If all goes well, the reply is like:

OK: OK
WOMBATID: 366253628
NUMBER: 301
VAR: A=1
VAR: B=2

You should make a note of the WOMBATID as it will be used in subsequent calls. If you call this API multiple times, the same call will be returned as long as the agent has one reserved call.

Error codes include:

  • AGENT_NOT_AVAILABLE: The agent is not present, or is busy o paused

  • AGENT_CANNOT_RESERVE: No available call for the agent to make - there is no running campaign that points to that agent where there is free space on the trunk and there is a call to be made, or the agent is busy elsewhere

Place a reserved call

In order to place a reserved call, you need to feed Wombat its WOMBATID:

http://127.0.0.1:8080/wombat/api/reserve/?op=dial&id=123456

Returns

  • DIALING: the call is being placed

  • ID_NOT_FOUND: No such ID

  • ID_FOUND_WRONG_STATE: The ID exists but the call is in a wrong state

Skip a reserved call

In order to skip a reserved call, you need to feed Wombat its WOMBATID:

http://127.0.0.1:8080/wombat/api/reserve/?op=skip&id=123456&withStatus=XXX

The parameter "withStatus" is optional and that will be the extStatus for the call being skipped. You can use this to get fine control on whether it is to be rescheduled and how.

Returns:

  • SKIPPED: the call was skipped

  • ID_NOT_FOUND: No such ID

  • ID_FOUND_WRONG_STATE: The ID exists but the call is in a wrong state

Live call details

You can get the details of a currently live or scheduled call.

http://127.0.0.1:8080/wombat/api/callinfo/?wid=WBT.1234

The parameter "wid" contains the WombatId. It can contain a prefix "WBT" as it is logged on QueueMetrics logs or it can be without it. As an alternative, you can pass the parameter "unqid" that contains an Asterik’s UniqueID for the call.

The method will:

  • look for the call on the Live calls table (returns "isLive" as true)

  • look for the call in the logs (returns "isLive" as false)

Wombat will return a JSON data structure of the format:

{
  "callFound" : true,
  "isLive": true,
  "callRecord" : {
    "callId" : 418760,
    "listId" : {
      "listId" : 95,
      "name" : "Z7_04142015",
      "isHidden" : false,
      "securityKey" : ""
    },
    "number" : "9377893386",
    "attributes" : [ {
      "isInput" : true,
      "attrName" : "FIRST NAME",
      "attrValue" : "KATRINA"
    } ]
  },
  "call" : {
    "hopperCallId" : 212783745,
    "number" : "9377893386",
    "numberId" : 418760,
    "state" : "IN_HOPPER",
    "trunk" : "ABC Trunk",
    "astServer" : "ABC",
    "trunk_size" : 40,
    "endpoint" : "QUEUE Zebra Seven",
    "campaignRun" : "Z7_1210 15.04.16 12:22:57",
    "tst_originate" : 0,
    "tst_connect" : 0,
    "tst_terminate" : 0,
    "astUnique" : "",
    "astChannel" : "",
    "nRetry" : 0,
    "statusExt" : "",
    "subEp" : "SIP/21071",
    "bridgedChan" : ""
  }
}

The most important items are:

  • callFound: whether a call with the ID was found or not

  • isLive: whether the returned result comes from a live call or from the logs (completed call)

  • call: the current details of the live call

  • callRecord: the details of the number being called - its current attributes and the list it belongs to.

System health

It is possible to receive a JSON structure with general information about the system. It is meant to be used by external monitoring systems that need to make sure WombatDialer is working.

http://127.0.0.1:8080/wombat/api/sysup/

The response will be similar to the one below:

{
  "state" : "WBTUP",
  "ramFreeMb" : 73,
  "ramTotalMb" : 139,
  "ramMaxMb" : 1818,
  "db_access_ms" : 4,
  "generatedOn" : "Mon Sep 29 16:04:00 CEST 2014",
  "version" : "0.8.0/#1234"
}

When this transaction is run, the database is accessed to make sure it is available. If the response block does not contain the string "WBTUP" the application is supposed to be down and in need of a restart.

Even when you get the state WBTUP, you know that the web application is available, but the dialer itself might be stopped, like when you start and stop it from the GUI. See Engine control.

JMX statistics

WombatDialer traces a wealth of information about its runtime performance as JMX beans that are available to JMX pollers. As JMX is notoriously hard to set up correctly, it is also possible to read the majority of that data using a webservice:

http://127.0.0.1:8080/wombat/api/sysup/jmx/

The response will be similar to the one below:

{
  "\"WD/AMI-127.0.0.1:15038#6.actionsSize\":value" : "0",
  "\"WD/AMI-127.0.0.1:15038#6.looptime\":50th" : "11.229",
  "\"WD/AMI-127.0.0.1:15038#6.looptime\":95th" : "12.64",
  "\"WD/AMI-127.0.0.1:15038#6.looptime\":count" : "166",
  "\"WD/AMI-127.0.0.1:15038#6.looptime\":max" : "17.977",
  "\"WD/AMI-127.0.0.1:15038#6.looptime\":min" : "10.04",
  "\"WD/AMI-127.0.0.1:15038#6.looptime\":rate_sec" : "15.484",
  "\"WD/AMI-127.0.0.1:15038#6.looptime\":rate_sec_oneminute" : "15.416",
  "\"WD/AMI-127.0.0.1:15038#6.msg.all\":value" : "ERROR",
  "\"WD/AMI-127.0.0.1:15038#6.msg.check\":value" : "ERROR",
  "\"WD/AMI-127.0.0.1:15038#6.msg.originate\":value" : "ERROR",
  "\"WD/AMI-127.0.0.1:15038#6.queue-size\":value" : "ERROR",
  "generatedOn" : "Fri Nov 15 14:56:14 CET 2019",
  "jmx_query_ms" : "28",
  "jvm_code_cache" : "18897472",
  "jvm_cpu_load" : "0",
  "jvm_daemon_threads" : "24",
  "jvm_heap_max" : "3817865216",
  "jvm_heap_used" : "141595248",
  "jvm_loaded_classes" : "6145",
  "jvm_nonheap_max" : "-1",
  "jvm_nonheap_used" : "61834768",
  "jvm_thread_count" : "25",
  "jvm_total_compilation_time" : "12576",
  "jvm_unloaded_classes" : "0",
  "wd-hlp.autorun:50th" : "0",
  "wd-hlp.autorun:95th" : "0",
  "wd-hlp.autorun:count" : "0",
  "wd-hlp.autorun:max" : "0",
  "wd-hlp.autorun:min" : "0",
  "wd-hlp.autorun:rate_sec" : "0",
  "wd-hlp.autorun:rate_sec_oneminute" : "0",
  "wd-hlp.email:50th" : "0",
  "wd-hlp.email:95th" : "0",
  "wd-hlp.email:count" : "0",
  "wd-hlp.email:max" : "0",
  "wd-hlp.email:min" : "0",
  "wd-hlp.email:rate_sec" : "0",
  "wd-hlp.email:rate_sec_oneminute" : "0",
  "wd-hlp.http:50th" : "0",
  "wd-hlp.http:95th" : "0",
  "wd-hlp.http:count" : "0",
  "wd-hlp.http:max" : "0",
  "wd-hlp.http:min" : "0",
  "wd-hlp.http:rate_sec" : "0",
  "wd-hlp.http:rate_sec_oneminute" : "0",
  "wd-log.cycles:50th" : ".003",
  "wd-log.cycles:95th" : ".022",
  "wd-log.cycles:count" : "115",
  "wd-log.cycles:max" : ".067",
  "wd-log.cycles:min" : ".001",
  "wd-log.cycles:rate_sec" : "9.752",
  "wd-log.cycles:rate_sec_oneminute" : "9.784",
  "wd-log.n_logs:value" : "ERROR",
  "wombat.all.brain:50th" : "4.662",
  "wombat.all.brain:95th" : "11.947",
  "wombat.all.brain:count" : "199",
  "wombat.all.brain:max" : "18.31",
  "wombat.all.brain:min" : ".004",
  "wombat.all.brain:rate_sec" : "16.871",
  "wombat.all.brain:rate_sec_oneminute" : "16.112",
  "wombat.all.msg-in:50th" : ".013",
  "wombat.all.msg-in:95th" : "3.632",
  "wombat.all.msg-in:count" : "199",
  "wombat.all.msg-in:max" : "159.502",
  "wombat.all.msg-in:min" : ".004",
  "wombat.all.msg-in:rate_sec" : "16.862",
  "wombat.all.msg-in:rate_sec_oneminute" : "16.112",
  "wombat.all.runafter:50th" : ".003",
  "wombat.all.runafter:95th" : ".185",
  "wombat.all.runafter:count" : "199",
  "wombat.all.runafter:max" : "7.832",
  "wombat.all.runafter:min" : ".002",
  "wombat.all.runafter:rate_sec" : "16.878",
  "wombat.all.runafter:rate_sec_oneminute" : "16.112",
  "wombat.brain.calls:50th" : "2.82",
  "wombat.brain.calls:95th" : "5.177",
  "wombat.brain.calls:count" : "198",
  "wombat.brain.calls:max" : "6.772",
  "wombat.brain.calls:min" : "1.183",
  "wombat.brain.calls:rate_sec" : "16.787",
  "wombat.brain.calls:rate_sec_oneminute" : "15.928",
  "wombat.brain.reschedule:50th" : "0",
  "wombat.brain.reschedule:95th" : ".013",
  "wombat.brain.reschedule:count" : "198",
  "wombat.brain.reschedule:max" : ".019",
  "wombat.brain.reschedule:min" : "0",
  "wombat.brain.reschedule:rate_sec" : "16.786",
  "wombat.brain.reschedule:rate_sec_oneminute" : "15.928",
  "wombat.brain.runs:50th" : ".004",
  "wombat.brain.runs:95th" : ".02",
  "wombat.brain.runs:count" : "198",
  "wombat.brain.runs:max" : ".051",
  "wombat.brain.runs:min" : ".002",
  "wombat.brain.runs:rate_sec" : "16.786",
  "wombat.brain.runs:rate_sec_oneminute" : "15.928",
  "wombat.hibernate.commit:50th" : ".576",
  "wombat.hibernate.commit:95th" : "3.366",
  "wombat.hibernate.commit:count" : "201",
  "wombat.hibernate.commit:max" : "12.056",
  "wombat.hibernate.commit:min" : ".242",
  "wombat.hibernate.commit:rate_sec" : "17.044",
  "wombat.hibernate.commit:rate_sec_oneminute" : "16.48",
  "wombat.hibernate.evict:50th" : ".001",
  "wombat.hibernate.evict:95th" : ".001",
  "wombat.hibernate.evict:count" : "201",
  "wombat.hibernate.evict:max" : ".003",
  "wombat.hibernate.evict:min" : "0",
  "wombat.hibernate.evict:rate_sec" : "17.052",
  "wombat.hibernate.evict:rate_sec_oneminute" : "16.48",
  "wombat.hibernate.flush:50th" : ".462",
  "wombat.hibernate.flush:95th" : "1.45",
  "wombat.hibernate.flush:count" : "198",
  "wombat.hibernate.flush:max" : "6.107",
  "wombat.hibernate.flush:min" : ".199",
  "wombat.hibernate.flush:rate_sec" : "16.806",
  "wombat.hibernate.flush:rate_sec_oneminute" : "15.928",
  "wombat.hibernate.openconnection:50th" : "155.41",
  "wombat.hibernate.openconnection:95th" : "155.41",
  "wombat.hibernate.openconnection:count" : "1",
  "wombat.hibernate.openconnection:max" : "155.41",
  "wombat.hibernate.openconnection:min" : "155.41",
  "wombat.hibernate.openconnection:rate_sec" : ".085",
  "wombat.hibernate.openconnection:rate_sec_oneminute" : ".184",
  "wombat.hibernate.save:50th" : "0",
  "wombat.hibernate.save:95th" : "0",
  "wombat.hibernate.save:count" : "0",
  "wombat.hibernate.save:max" : "0",
  "wombat.hibernate.save:min" : "0",
  "wombat.hibernate.save:rate_sec" : "0",
  "wombat.hibernate.save:rate_sec_oneminute" : "0",
  "wombat.refill.new:50th" : "0",
  "wombat.refill.new:95th" : "0",
  "wombat.refill.new:count" : "0",
  "wombat.refill.new:max" : "0",
  "wombat.refill.new:min" : "0",
  "wombat.refill.new:rate_sec" : "0",
  "wombat.refill.new:rate_sec_oneminute" : "0",
  "wombat.refill.retry:50th" : "0",
  "wombat.refill.retry:95th" : "0",
  "wombat.refill.retry:count" : "0",
  "wombat.refill.retry:max" : "0",
  "wombat.refill.retry:min" : "0",
  "wombat.refill.retry:rate_sec" : "0",
  "wombat.refill.retry:rate_sec_oneminute" : "0",
  "wombat_version" : "0.0.0/#1234"
}

Data is measured (depending on what it is) as:

  • …​:50th: the median value of a timer.

  • …​:95th: the 95th percentile of a run time

  • …​:count: the number of times a time was measured

  • …​:max: maximum duration

  • …​:min: minimum duration

  • …​:rate_sec: number of times per second the timer is triggered

  • …​:rate_sec_oneminute: number of times per second the timer was triggered in the last minute

The fields have the following meanings (as better explained in Monitoring a running instance):

  • General Wombat statistics:

    • wombat.all.brain: how long Wombat spent making decisions

    • wombat.all.msg-in: how long Wombat spent processing incoming messages

    • wombat.all.runafter: how long was spent running periodic jobs

  • Decision maker:

    • wombat.brain.brain: how long was spent making decisions on calls to dial

    • wombat.brain.calls: how long was spent placing calls

    • wombat.brain.reschedule: time spent evicting or rescheduling calls

    • wombat.brain.runs: how long was spent starting and stopping runs

  • Database layer:

    • wombat.hibernate.commit: time committing changes

    • wombat.hibernate.evict: time evicting stale objects from cache

    • wombat.hibernate.flush: time flushing database changes

    • wombat.hibernate.save: time saving new objects

    • wombat.hibernate.refresh: time doing a deep refresh of campaigns

    • wombat.hibernate.openconnection: how many connections were open. Usually only increments only on restarts.

  • Fetching of calls from lists:

    • wombat.refill.new: time spent reading new calls from lists

    • wombat.refill.retry: time spent reading calls from retry cache

    • wombat.refill.blacklist: time spent checking blacklists

  • For each PBX Head:

    • (AMI head name).actionsSize: actions to process

    • (AMI head name).all: all messages

    • (AMI head name).check: number of liveness checks

    • (AMI head name).originate: number of originates

    • (AMI head name).queue-size: current dialing queue size

    • (AMI head name).fromAsterisk: number of events received from Asterisk

    • (AMI head name).fromMaster: number of commands received from Wombat

    • (AMI head name).connected: 1 if connected, 0 if disconnected

  • Helpers

    • wd-hlp.autorun: deferred actions

    • wd-hlp.email: time spent sending emails

    • wd-hlp.http: time spent doing HTTP notifications

  • Logger

    • wd-log.cycles: commit timer

    • wd-log.n_logs: number of logs received to be written

  • JVM stats

    • All stats beginning with jvm_ are internal JVM stats

  • Misc

    • generatedOn: when the request was generated

    • jmx_query_ms: total JMX query time

    • wombat_version: the version of Wombat

All times are in milliseconds.

Adding a license key

You may add a license key to a working WombatDIaler system or query its current licensing. This is meant for dynamic, short-term key assignment.

The following call adds key "ABCD.1234" and activates it:

http://127.0.0.1:8080/wbt/api/addkey/?op=add&key=ABCD.1234

The response will display the state of all installed keys on the system:

{
  "requestId" : "",
  "lockUntilComplete" : true,
  "responseStatus" : "OK",
  "responseMessage" : "",
  "toPanel" : "nopanel",
  "responseLog" : null,
  "keyText" : "",
  "isAdd" : true,
  "lLic" : [ {
    "licenseId" : "DEMO070",
    "licensedTo" : "DEMO / WombatDialer 070",
    "startsOn" : "2012-12-18",
    "endsOn" : "2013-12-18",
    "currState" : "REVOKED",
    "nChannels" : 2
  }, {
    "licenseId" : "LLX1234",
    "licensedTo" : "LLX1234 / Camp3x",
    "startsOn" : "2015-07-02",
    "endsOn" : "2016-07-10",
    "currState" : "ACTIVE",
    "nChannels" : 8
  } ],
  "l_px" : "257377543787",
  "l_ch" : 8,
  "processor" : "ch.loway.app.wombat.ui.svr.LicensingProcessor"
}

The value "l_ch" is the total number of licensed channels currently active, while details of existing licenses can be seen from "lLic".

The JSON configuration API

WombatDialer exposes a JSON configuration API that lets you fully configure WombatDialer - as you would through the GUI - using a JSON API. This makes it easy to deploy multiple instances of Wombat for very large systems handing thousands of channels at once. It also lets you configure campaigns to be run automatically, each with the correct configuration.

We have some tutorial scripts that let you see the concepts explained below in action - they are located at https://github.com/Loway/OpenWombatDialerAddOns under the folder JSON_Configuration_API. Get a copy and make sure you understand how they work before writing your own.

General concepts

JSON API requests can be of four kinds:

  • LIST: returns a paged list of objects. You can tell Wombat the page you want to read by specifying a start offset and an expect result size. Plus, you can optionally send along a filter parameter that will be used to further refine the results.

  • EDIT: you push a JSON object to be added to the Wombat database. If you define a primary key for the object, the object will be updated; if the key is empty (as set to JSON 'null') then the object will be added. A basic sanity check will be performed on inserts and updates. The result of an update is typically the first page of results.

  • DELETE: The JSON object will be deleted. The object must include a primary key to tell Wombat which object is to be deleted.

  • MOVE UP/DOWN: on some lists (e.g. reschedule rules) it is possible to move an object up or down in the list.

You specify the operation that is to be performed by passing the following parameters:

  • 'mode': shall be 'L' for list, 'E' for editing, 'D' for delete, 'MU' and 'MD' to move the object up and down. If not specified, listing is assumed.

  • 'data': the JSON object itself. This is not needed when listing.

  • 'parent': some objects depend on other objects being selected first (see the description of dependent objects below). This is the primary key of the main object that holds those sub-objects.

  • 'from': the beginning of the list to be returned. If missing, zero is assumed.

  • 'items': the maximum number of items to be returned. If missing, 100 is used.

  • 'query': a string that is to be searched in text fields. Depends on the object - this behaves like the search boxes you see on the GUI.

  • For any JSON query, you need to send Wombat an active user who holds the grants to perform the required operation, passing it as a Basic HTTP authentication.

For example, the following query lists available trunks through the popular 'curl' command-line HTTP client:

curl  --user demoadmin:demo -i -X POST "http://127.0.0.1:8080/wombat/api/edit/trunk/?mode=L"

The result will be something like:

{
  "status" : "OK",
  "error" : "",
  "results" : [ {
    "sys_dt_creazione" : "2015-03-18 17:02:46",
    "sys_user_creazione" : "Demo Admin",
    "sys_dt_modifica" : "",
    "sys_user_modifica" : "-",
    "trunkId" : 1,
    "astId" : {
      "sys_dt_creazione" : "2015-03-18 17:02:29",
      "sys_user_creazione" : "Demo Admin",
      "sys_dt_modifica" : "2015-03-23 15:49:45",
      "sys_user_modifica" : "Demo Admin",
      "id" : 1,
      "description" : "Asterisk Server",
      "ipAddress" : "127.0.0.1",
      "login" : "hello",
      "password" : "kitty",
      "port" : 5038,
      "unit_len" : 50,
      "msg_per_unit" : 5,
      "securityKey" : "",
      "state" : "DOWN"
    },
    "name" : "My Trunk",
    "dialstring" : "Local/${num}@from-internal/n",
    "capacity" : 10,
    "securityKey" : ""
  } ],
  "allResults" : 1,
  "startFrom" : 0,
  "nRecords" : 1,
  "timeMs" : 12
}

The JSON response always includes:

  • a 'status' that should be 'OK' if all went well. If something went wrong, you will have a description in the 'error' field (usually a Java stack trace).

  • a list named 'results' that may include zero or more elements. These are the objects you are querying.

  • an estimated size for all possible results to this query passed as 'allResults'.

  • the point in the list of results from where results are sent back ('startFrom') and the amount of records on the current page ('nRecords').

  • how long the operation took on the server, expressed in milliseconds, as 'timeMs'.

If you want to insert or upgrade an object, you first need to save it as a JSON entity. All objects specify a primary key that you must use to make sure that the object stays the same during updates. Any access information - who created and updated the object, and when - is updated automatically.

So to change the name of the previous trunk, you could create a file called 'newtrunk.json' that contains:

{
    "trunkId" : 1,
    "astId" : {
      "id" : 1
    },
    "name" : "NewName",
    "dialstring" : "Local/${num}@from-internal/n",
    "capacity" : 10,
    "securityKey" : ""
}

Note that:

  • We use the 'trunkId' we were supplied by reading, as it is the primary key for this object.

  • All user and time-stamp information were removed. There is no harm in leaving it in, but it will not be used when updating.

  • Any inner objects (like, in our example, the Asterisk server) will NOT be updated on the same query. It is safe to remove everything but the primary key (in our case 'id'), as the object will be loaded by primary key in order to be correctly associated. You cannot associate to objects that are not existing at the moment the request is issued.

We can then push this new object by issuing:

curl  --user demoadmin:demo -i -X POST --data-urlencode data@newtrunk.json "http://127.0.0.1:8080/wombat/api/edit/trunk/?mode=E"

Please note that you can mix-and-match GET and POST parameters as you best see fit.

Working with dependent objects

Dependent object are objects that have a parent and that make no sense without specifying their parent. For example, reschedule rules depend on a campaign and have no meaning outside of it. To work with a dependent object, you must specify the primary key of its parent, like in:

curl  --user demoadmin:demo -i -X POST  "http://127.0.0.1:8080/wbt/api/edit/campaign/reschedule/?parent=1"

The response will be like:

{
  "status" : "OK",
  "error" : "",
  "results" : [ {
    "rescheduleRuleId" : 1,
    "status" : "RS_NOANSWER",
    "statusExt" : "",
    "maxAttempts" : 1,
    "retryAfterS" : 120,
    "mode" : "FIXED",
    "idx" : "1",
    "campaignId" : 1
  } ],
  "allResults" : 1,
  "startFrom" : 0,
  "nRecords" : 1,
  "timeMs" : 18
}
Dependent objects always appear in the API "below" their main object, as in "/edit/campaign/reschedule/".

In order to insert a new rule for campaign #1, you would create a new entry like:

{
    "rescheduleRuleId" : null,
    "status" : "RS_NOANSWER",
    "statusExt" : "",
    "maxAttempts" : 1,
    "retryAfterS" : 120,
    "mode" : "FIXED",
    "campaignId" : 1
}

and push it like:

curl  --user demoadmin:demo -i -X POST --data-urlencode data@rrnew.json "http://127.0.0.1:8080/wbt/api/edit/campaign/reschedule/?mode=E&parent=1"

Note that the primary key of the parent appears in both the object - as 'campaignId' - and as 'parent' in the JSON query.

If we want to move the object up (MU) or down (MD), we can edit the 'rrnew.json' file to set the new 'rescheduleRuleId' and:

curl  --user demoadmin:demo -i -X POST --data-urlencode data@rrnew.json "http://127.0.0.1:8080/wbt/api/edit/campaign/reschedule/?mode=MU&parent=1"

And to delete it we would:

curl  --user demoadmin:demo -i -X POST --data-urlencode data@rrnew.json "http://127.0.0.1:8080/wbt/api/edit/campaign/reschedule/?mode=D&parent=1"

Using the JSON API in practice

  • Issue a list query using the shell to see available objects and how they are represented.

  • To create a new object, create it manually from the GUI and read it through JSON to see how it "looks like".

  • The primary key for any object appears in the object definition

  • When listing objects, always take advantage of paged queries if you think that there might be more than a hundred objects returned. Reading and serializing thousands or tens of thousands of elements at once is not a good idea! On the other hand, returning hundreds of thousands of objects, 500/1000 at a time, is usually safe. Remember that it is important to avoid running extremely expensive operations that might impact the dialer itself by using too much CPU, contending the database or - worse - crashing the JVM by exhausting all of its available RAM! So while it is hard to put general rules that depend on the kind of hardware you are running and on the current load, it is always better to err on the side of caution.

  • Objects are usually returned in descending order of insertion, so your shiny new object is typically the first one returned.

Available JSON APIs

The URL expressed in the table below are only the URL fragments that have to be appended to your WombatDialer API main URL - for example, if the table lists 'asterisk' as the fragment, the URL will be 'http://127.0.0.1:8080/wbt/api/edit/asterisk/' (note the mandatory trailing slash).

Table 1. JSON API
URL fragment Description Primary key name Depends on? Moves Up/Down Security key

asterisk

Asterisk servers

id

-

No

ADMIN

ep

End-points

epId

-

No

ADMIN

trunk

Trunks

trunkId

-

No

ADMIN

oh

Opening Hours

openingHourId

-

No

OPHR

campaign

Campaigns

campaignId

-

No

CAMP

campaign/ep

EPs on campaigns

-

Campaign

No

CAMP

campaign/trunk

Tks on campaigns

-

Campaign

No

CAMP

campaign/list

Lists on campaigns

cclId

Campaign

Yes

CAMP

campaign/reschedule

Reschedule Rules

rescheduleRuleId

Campaign

Yes

CAMP

campaign/disposition

Cmp. Dispositions

dispositionId

Campaign

Yes

CAMP

campaign/oh

Cmp. Opening Hours

cohId

Campaign

Yes

CAMP

list

A call list

listId

-

No

LIST

list/record

Numbers on a list

callId

List

No

LIST

list/logs

Called numbers

id

List

No

LIST

Notes:

  • OpeningHours are composite objects, so any inner rule is handled as a private JSON structure.

  • Campaign EPs and Trunks are checked for uniqueness, as they do not have an inner order. Wombat will make sure that they cannot be added more than once.

  • A field named 'idx' is used to track the position of items in a sorted list.

The JSON Live and Reporting API

The JSON live API lets you query the state of the system as seen from the Live and Reports page.

The following table shows a succinct description of what is available.

URL fragment Description Result key Depends on? Security key

live/calls

Live calls

-

-

RT

live/runs

Active campaigns

-

-

RT

reports/runs

Runs for period

hoppercampId

from / to

REPORT

reports/stats

Stats for runs

-

id

REPORT

reports/logs

Logs for runs

-

id/from/items

REPORT

Live Calls

You can get a list of live calls and available campaigns by issuing:

curl  --user demoadmin:demo -i -X POST "http://127.0.0.1:8080/wombat/api/live/calls/"

The results includes:

  • A list of possible campaigns, their IDs and whether they are running

  • A list of live calls, under "hopperState"

For example:

{
  "status" : "OK",
  "error" : "",
  "timeMs" : 9,
  "result" : {
    "requestId" : "",
    "lockUntilComplete" : false,
    "responseStatus" : "OK",
    "responseMessage" : "",
    "toPanel" : "API_CALL",
    "responseLog" : null,
    "campaigns" : [ {
      "campaignId" : 1,
      "name" : "My Campaign",
      "isRunning" : true,
      "secKey" : ""
    } ],
    "asterisks" : [ ],
    "hopperState" : [ {
      "hopperCallId" : 554418377,
      "number" : "12345678991",
      "state" : "DIALLING",
      "trunk" : "MyTrunk",
      "astServer" : "A44",
      "trunk_size" : 10,
      "endpoint" : "ep8",
      "campaignRun" : "test 15.03.18 17:04:32",
      "tst_originate" : 1427475059650,
      "tst_connect" : 0,
      "tst_terminate" : 0,
      "astUnique" : "1427475059.7189",
      "astChannel" : "Local/12345678991@wdtrunk-00000dcf;1",
      "nRetry" : 0,
      "statusExt" : "",
      "subEp" : "",
      "bridgedChan" : ""
    }]
  }
}

Live Campaigns

You can get information on Live campaigns by issuing:

curl  --user demoadmin:demo -i -X POST "http://127.0.0.1:8080/wombat/api/live/runs/"

The result includes an item called "campaigns" where you can see the live status:

{
  "status" : "OK",
  "error" : "",
  "timeMs" : 2404,
  "result" : {
    "requestId" : "",
    "lockUntilComplete" : false,
    "responseStatus" : "OK",
    "responseMessage" : "",
    "toPanel" : "API_CALL",
    "responseLog" : null,
    "action" : "NONE",
    "hopcampId" : 0,
    "campaignId" : 0,
    "campaigns" : [ {
      "name" : "cccd",
      "startedAt" : "15.03.18 17:04:32",
      "campaignId" : 1,
      "hoppercampId" : 2,
      "priority" : 10,
      "state" : "IDLE",
      "n_calls_completed" : 0,
      "n_open_retries" : 0,
      "n_calls_attempted" : 0,
      "n_est_remaining_calls" : 0,
      "secondsSinceStarted" : 771835,
      "listName" : "#0",
      "listPos" : 0,
      "timeDow" : "1234567",
      "timeStartHr" : "000000",
      "timeEndHr" : "235959",
      "predictiveModel" : "OFF",
      "boostFactor" : 1.0,
      "dowAsString" : "Su | Mo | Tu | We | Th | Fr | Sa"
    } ]
  }
}

Reports: runs in period

This call gets you a list of runs (identified by their "hoppercampId") that were made in a specific period:

curl  --user demoadmin:demo -i -X POST "http://127.0.0.1:8080/wbt/api/reports/runs/?from=2000-01-01.00:00:00&to=2020-01-01.00:00:00"

Both the "from" and "to" parameters are mandatory.

The result is a list of runs:

{
  "status" : "OK",
  "error" : "",
  "timeMs" : 69,
  "result" : {
    "requestId" : "",
    "lockUntilComplete" : true,
    "responseStatus" : "OK",
    "responseMessage" : "",
    "toPanel" : "API_CALL",
    "responseLog" : null,
    "dateFrom" : 946681200000,
    "dateTo" : 1577833200000,
    "campaignsAndRuns" : [ {
      "name" : "test",
      "startedAt" : "15.03.18 17:04:32",
      "campaignId" : 1,
      "hoppercampId" : 2,
      "priority" : 10,
      "state" : "RUNNING",
      "n_calls_completed" : 91,
      "n_open_retries" : 109,
      "n_calls_attempted" : 91,
      "n_est_remaining_calls" : 9911,
      "secondsSinceStarted" : 782167,
      "listName" : "appero",
      "listPos" : 0,
      "timeDow" : "1234567",
      "timeStartHr" : "000000",
      "timeEndHr" : "235959",
      "predictiveModel" : "OFF",
      "boostFactor" : 1.0,
      "dowAsString" : "Su | Mo | Tu | We | Th | Fr | Sa"
    } ]
  }
}

Report: stats for runs

This call runs statistics for a set of runs, identified by the parameter "id" that can be present multiple times.

curl  --user demoadmin:demo -i -X POST "http://127.0.0.1:8080/wbt/api/reports/stats/?id=1&id=7&id=123"

The result is a set of stats by call type:

{
  "status" : "OK",
  "error" : "",
  "timeMs" : 8,
  "result" : {
    "requestId" : "",
    "lockUntilComplete" : true,
    "responseStatus" : "OK",
    "responseMessage" : "",
    "toPanel" : "API_CALL",
    "responseLog" : null,
    "campaignsToConsider" : [ 1, 7, 123 ],
    "statsOut" : [ {
      "gbCampaignId" : 1,
      "gbState" : "RS_BUSY",
      "gbTrunk" : "A44 NewName",
      "gbStateExt" : "",
      "nCalls" : 90,
      "totWaitPre" : 10,
      "totWaitAfter" : 271,
      "totTalk" : 0
    }, {
      "gbCampaignId" : 1,
      "gbState" : "RS_NUMBER",
      "gbTrunk" : "A44 NewName",
      "gbStateExt" : "",
      "nCalls" : 1,
      "totWaitPre" : 0,
      "totWaitAfter" : 0,
      "totTalk" : 0
    } ]
  }
}

Reports: logs by runs

Given a set of runs, gets the details of calls placed.

Parameters:

  • 'from': the start of results in the log set

  • 'items': how many logs you want back (page length)

  • 'id': the run ids - can be repeated multiple times

curl  --user demoadmin:demo -i -X POST "http://127.0.0.1:8080/wbt/api/reports/logs/?from=0&items=3&id=1&id=2&id=123"
Do not forget to set "sane" paging criteria, to avoid getting a result set that’s too large for available memory.

The results look like:

{
  "status" : "OK",
  "error" : "",
  "timeMs" : 12,
  "results" : [ {
    "dim_id" : 1,
    "listId" : 1,
    "listName" : "GoodList",
    "trunkId" : 1,
    "trunkName" : "NewName",
    "astId" : 1,
    "astName" : "",
    "epId" : 4,
    "epName" : "ep7",
    "campId" : 1,
    "campName" : "test",
    "runId" : 2,
    "runName" : "15.03.18 17:04:32",
    "runStarted" : 1426694673000,
    "id" : 1,
    "callId" : 1,
    "callNumber" : "1",
    "numberDialed" : "1",
    "attributes" : "IN:puts ^OUT:put",
    "attempted" : 1427474910000,
    "waitPre" : 1,
    "waitAfter" : 111,
    "talk" : 0,
    "statusCode" : "RS_NUMBER",
    "astChannel" : "",
    "astUnique" : "",
    "nRetry" : 0,
    "statusExt" : "",
    "nextRetry" : 0,
    "wombatId" : "515144647",
    "subEp" : "",
    "bridgedChannel" : ""
  } ],
  "allResults" : 91,
  "startFrom" : 0,
  "nRecords" : 3
}
The result set is the same you’d get for the 'edit/list/logs' call, but with different search criteria.

HTTP/S life-cycle notifications of calls

In order to enable HTTP life-cycle notifications, you must set a URL in your campaign definition that points to an HTTP script. This causes WD to POSTs the result of each call to your HTTP server.

Both "http" and "https" URLs are allowed.

On the server, you’ll have a script that accepts notification, like the simple PHP script that follows:

<?
$out = "";
foreach($_POST as $name => $value) {
   $out .= "$name:$value ";
}
print($out);
error_log("RQ: $out",0, "", "");
?>

The script above basically logs all activity on the HTTP error log. What you get is a sequence of calls like:

RQ: num:5551234 reschedule:0 I_MM:30 extstate:
     state:RS_REJECTED I_HH:10 retry:0
RQ: num:5556785 reschedule:0 I_MM:00 extstate:
     state:RS_REJECTED I_HH:11 retry:0
RQ: num:5552012 reschedule:0 I_MM:30 extstate:
     state:RS_REJECTED I_HH:11 retry:0
RQ: num:5551234 reschedule:0 I_MM:30 extstate:1
     state:TERMINATED I_HH:10 O_APPT:APPE retry:0
RQ: num:5556785 reschedule:300 I_MM:00 extstate:
     state:RS_NOANSWER I_HH:11 retry:0
RQ: num:5552012 reschedule:0 I_MM:30 extstate:2
     state:TERMINATED I_HH:11 O_APPT:PSYC retry:0
RQ: num:5556785 reschedule:300 I_MM:00 extstate:0
     state:TERMINATED I_HH:11 retry:1
RQ: num:5556785 reschedule:300 I_MM:00 extstate:0
     state:TERMINATED I_HH:11 retry:2
RQ: num:5556785 reschedule:300 I_MM:00 extstate:0
     state:TERMINATED I_HH:11 retry:3
RQ: num:5556785 reschedule:0 I_MM:00 extstate:0
     state:TERMINATED I_HH:11 retry:4

You can see that for each call:

  • num is set to the main number dialed and currentnum the number actually dialed (that might be different when using multinumbers).

  • listname is the list that the number was taken from

  • state is the call state at its completion

  • extstate is the call’s extended state, if present

  • retry is the retry counter

  • reschedule is set to the time in seconds to be waited before a reschedule is attempted; if no reschedule is required, it will be set to zero

  • asteriskid is the current Asterisk-id of the call

  • wombatid is the WombatId as it appears on the queue_log

  • all input attributes (the ones you set with the telephone number) are passed along prepended by I_

  • all output attributes (the ones you set in the Asterisk dialplan), if any, are passed along prepended by O_

This way you can easily create an integration script that stores the results of the call on a database or passes them along for further processing.

The HTTP notification server works asynchronously on a dedicated thread, so it is possible that there is some latency if your script has long completion times. The notification server does not retry on errors.