Asterisk’s REST Interface (ARI) in both Asterisk 12 and 13 has the ability to originate (create) outgoing channels. The functionality in ARI mirrors that of the “originate” CLI command, AMI action and dialplan applications. In its use, it creates, in one operation, a channel that is setup, dialed, and directed to a location upon answer. The big limiting factor is that all of these things happen in a single operation, and an answer is a required part of the process.
In Asterisk 14, much more flexibility is provided by splitting the operation in two parts: channel creation and channel dialing.
POST /channels/create
As you might imagine, the create operation will create an outgoing channel; but, unlike the regular originate operation, the channel will be immediately placed into your ARI application, without being dialed. While in the ARI application, you can perform operations on it that logically make sense. Any that don’t will respond with an error – don’t try to play a sound file to it, it hasn’t been dialed yet, silly. If your outgoing channel is the result of an incoming channel, you may want to connect the two in a bridge in case the outgoing channel provides progress in the form of media – inband ringing, or a friendly phrase saying the call could not be connected.
The operation takes a very slimmed down list of arguments in comparison to the origination one; just those required to create an outgoing channel and location information, nothing more. In fact, arguments controlling variables or caller ID are not present, as these should be set using the variable operation before dialing. You might also notice that you can not send created channels to the dialplan. This is because the dialplan, and dialplan applications, were not written to take into account created, but un-dialed channels, and thus do not support them. You can only send a created channel to an ARI application.
Early Bridging
The act of placing channels into a bridge before one has answered is referred to as “early bridging”. The bridging functionality in Asterisk has been changed to transparently, without any effort on your part, handle this case and do “the right thing.” You need only use the same operations that you would have used previously on answered channels to add them to the bridge.
A common scenario would be the following:
- Create an outgoing channel
- Set variables and callerid on the outgoing channel
- Create a bridge
- Add the calling channel to the bridge
- Add the outgoing channel to the bridge
- Dial the outgoing channel
This mirrors that of Asterisk’s Dial() application and ensures that any early media is conveyed. Building on this as a base allow you to implement many of the features of app_dial or even create new ones more easily.
POST /channels/{channelId}/dial
Once a channel has been created and you have performed any operations you want on it, you can actually perform the dial operation by calling “dial” on it. Dial places the actual outgoing call and you will receive events as they occur.
Give It A Try
As of this writing Asterisk 14.0.0-beta1 is available which includes this functionality. If you’ve been wanting more control over the dialing process in your ARI application, the future is now. If you experience any issues please submit an issue on the issue tracker so we can squash it before release.
34 Responses
Does this mean that I can finally replace AGI’s with ARI?
That depends! As ARI doesn’t provide access to run arbitrary dialplan applications you have to use the primitives provided to do what you need. If they fit your needs – then yes!
Hi Joshua,
Currently I have Asterisk 11 running on a production server and communicating with my c++ application on linux using AMI / ARI.
2 weeks ago I installed Asterisk 13 in another server to check if I can upgrade my production server from Asterisk 11 to Asterisk 13 and use the ARI communication. So in order to evaluate that, i created a client application using c++. Unfortunatelly it is very hard to find examples on c++, but i managed to make it work. I created the following archtecture:
1- WebSocketClient which performs the handshake with Asterisk 13 (i.e.)
“GET /ari/events?api_key=fabio_ari:123&app=hello-fabio”
“GET /ari/events?api_key=fabio_ari:123&app=hello-fabio HTTP/1.1 \nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Key: IidMJmdoGe4kYu0+1VlrvQ==\nSec-WebSocket-Version: 13 \n\n”
2- For all the other operations I use an httpClient i implemented to send POSTs, DELETEs, etc… For example the Originate
“POST /ari/channels?endpoint=PJSIP/111&extension=s&context=fabio_test HTTP/1.1”
Sorry for this information. Everything is working fine so far. I am able to create channels, bridges, hangup on a channel, shutdown a bridge, etc…
In fact I only have one question for you.
Can I use only the WebSocket connection for everything?
I mean, can i send POST, GET and DELETE using my websocket connection with Asterisk? Or do I need to use a connection for each POST, GET, … using an HTTP Client?
I really want to understand what should i implement in my application. So far i couldn’t start an Originate thru the websocket, I can only do it using HTTP. When it gets to the DialPlan and I call the STASIS then i get updates for that channel on my websocket. But still, I need to know if i really need to send those HTTP messages.
Thanks in advance!
Fabio
The WebSocket connection is strictly for events from Asterisk to applications. It can not currently be used for sending commands, these have to be done using a HTTP client with HTTP requests.
Thanks Joshua for the quick response and confirmation.
Hi Joshua,
Is there a command to destroy all bridges in CLI ?
Or the only way is using ARI and GET all bridges and do a DELETE on each one of them?
I would prefer a quick command to run in CLI to delete all the bridges like “hangup request all” for the channels.
There is no CLI command to do this, it’s up to the ARI application to do so as it has ownership.
Hi Fabio,
if you’re using modern C++ you could have a look at my library aricpp: https://github.com/daniele77/aricpp
Hi Daniele,
Thanks very much for sharing your library. It looks amazing, congrats ! When I started developing it, I couldn’t find any good examples in c++ and I am glad you shared that with me.
It shows me I chose the correct approach when using boost::asio and boost::beast. I was afraid of using boost::beast as it is not released yet with latest boost, but I read it will be released in the next version (probably available on december). Anyway I am already using it and it looks good enought for me so far.
I am using c++11 but its only a tech example to prove/test Asterisk 13. I am testing performance against Asterisk 11 and so far I am getting very good results.
Have you performed such high load tests ?
Thanks in advance,
Fabio Carvalho
Hi Fabio,
these days I’m just writing a post for this blog that will explain in detail the design choices of the library (e.g., boost, beast, async architecture) and the performance issues I encountered.
To cut it short:
I chose beast because it’s the only http/websocket asynchronous library available and because it follows boost ASIO idioms. Actually, I find the API poor designed and error prone, but I hope they will improve it when finally beast will become part of boost 1.66 .
I did stress tests using a sip load generator (sipp) and asterisk 14. Unfortunately, I couldn’t reach a high rate of calls per seconds because of a “performance bug” I discovered in asterisk sources. I opened a ticket for that:
https://issues.asterisk.org/jira/browse/ASTERISK-26771
basically, in the worst case, ARI http requests can take 200ms to be taken into account by asterisk.
So, I basically could use the sip traffic generator only to prove my library is correct, but not to optimize aricpp for speed.
Hi Joshua,
I am afraid I might face a limitation problem in my implementation using ARI.
I asked Daniele a few days ago on another article in this blog “http://blogs.asterisk.org/2017/11/01/aricpp-ari-library-modern-c/” but he hadn’t face the issue i am having which is:
I am worried about the max length of the content-length. When i send a GET ari/channels, i need to specify the size of the buffer to read the http response. For now i just used 4096 as I read in some posts (i.e. https://issues.asterisk.org/jira/browse/ASTERISK-24883)
Do you know if the request will fail in case it exceeds the 4096 length?
In my GET ari/channels request, i might have several channels active on asterisk and that size will might exceed the 4096.
Questions:
1) Do you know how asterisk will behave if it exceeds 4096 length response message? If it will fail to answer, or if it will send me a second message wth the remaining channels?
2) I have a server application which will need to connect to asterisk and retrieve all the channels already active. Let’s say my server crashed and i have several calls in progress. When i restore my server, i will need to sync it with asterisk (meaning… i will need to check with asterisk what channels are active as soon as it restarts). Is there an alternative to the GET ari/channels ?
Thanks in advance,
Fabio
I noticed in the ari documentation the following option:
“HTTP
Asterisk’s HTTP server now supports chunked Transfer-Encoding. This will be automatically handled by the HTTP server if a request is received with a Transfer-Encoding type of chunked.”
Maybe that’s what i am looking for. What do you think?
I don’t have experience with the HTTP protocol at that level, specifically with chunked encoding, so I can’t comment on that. You might have better luck on the asterisk-app-dev mailing list with that kind of question from people who have experience.
As for if your server application crashes – you either need to maintain state such that it knows what channels exist and what to do after it restarts, or you will have to query the system to try to determine the state. The GET you mention is the only method of retrieving the list of channels.
Hi Joshua,
I just ran another test with 60 active channels and it worked fine. So maybe there is no limitation on the content-length
60 active channels
60 active calls
ContentLength ==================== 24896
And I was able to get all the channels information and parse them right out of the json to a ptree.
Thanks again
Hi Joshua,
I have a doubt related with the stasis.
In my application, when I start a call with a customer ( not necessarily the agent who started the call will be talking to the customer ). So in my dialplan I start a Stasis and check in my server which agent will talk to the customer. When the server decides the agent, I set a variable in the channel and send a continue to the channel.
After that customer joins the confbridge with that selected agent.
But after testing that, I noticed that the continue will send a StasisEnd and I won’t get notifications on the websocket anymore.
Is there a way to do continue on the dial plan and keep the stasis up?
I know I can also manage the bridges using ARI, but I didn’t want to change things that are already working like the confbridge or record the call using MixMonitor for example.
Thanks in Advance,
Fabio
You can subscribe to the channel[1] before using continue and you will continue to receive events for it even after leaving the stasis application. You can not, however, keep it in the stasis application and you won’t have control of it.
[1] https://wiki.asterisk.org/wiki/display/AST/Asterisk+15+Applications+REST+API#Asterisk15ApplicationsRESTAPI-subscribe
Thanks for the suggestion, it worked for me.
Do you know if it’s possible to filter the events to be sent? For example, I don’t want to receive verboses or VarSetChanges.
I know that for AMI we can filter them using manager.conf.
There is no filtering mechanism like AMI has as of this time.
Hi Joshua,
Now i am working with Park/Unpark calls. In Asterisk 11 I am able to make it work.
I use a phone_id to put the call in a parking lot
exten => s,n,Set(PARKINGEXTEN=${sc_phone_id})
exten => s,n,Park(,,,,s)
And when I unpark that call, I send an originate to that phone_id. (Local/12345@parkedcalls)
I tried to do the same in Asterisk 13, but when I send the originate thru http with the same parameter (Local/12345@parkedcalls), it doesn’t continue the dial plan after the Park(). It starts a new dial plan from the begining.
Log when trying to unpark 2000277. (Local/2000277@parkedcalls)
— Called 2000277@parkedcalls
— Executing [2000277@parkedcalls:1] ParkedCall(“Local/2000277@parkedcalls-00000001;2”, “default,2000277”) in new stack
— Local/2000277@parkedcalls-00000001;1 answered
— Executing [s@sc_customer:1] Verbose(“Local/2000277@parkedcalls-00000001;1”, “1,Starting call to Phone ID 2000277: “) in new stack ===> This verbose is set in the begining of my dialplan
Do you know if something has changed from Asterisk 11 to ASterisk 13 regarding park/unpark?
Instead of configuring on features.conf I configure on res_parking.conf (after my changes are done, I restart asterisk service as expected).
res_parking.conf (some information that might be usefull)
parkpos => 2000001-2000900
context => parkedcalls
parkingtime => 300
comebacktoorigin = yes (tried both yes and no)
Parking was changed as part of Asterisk 13. I personally have no experience with it though. I’d suggest using the community resources, such as the forum site[1], for questions like this.
[1] https://community.asterisk.org/
Ok, great, I will try to contact them !
Hi Joshua,
Is it possible to work with Confbridge using ARI ?
In my current application using AMI I have the following:
1- The agent registers to asterisk and stays in a Confbridge created with his agent_id
2- He makes a call to a customer and as soon as the call is answered, the customer joins the confbridge of that agent.
3- The agent may want to send a voicemail. In this case, he sends a ConfbridgeKick on the channel of that customer in order to continue with the dialplan.
4- The voicemail is played and the agent is able to make other phonecalls as the confbridge doesn’t have that customer anymore and another customer can join it.
Now using ARI, the concept of Confbridge and Bridge is different as far as I understood.
Confbridge is related with AMI and Bridge is related with ARI.
Question:
Can I still send a ConfbridgeKick using ARI ? I checked and there is a user event I can send a POST. https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Events+REST+API
How does it work? What kind of events are those?
I don’t know if it will help me yet.
If not, what to you suggest?
I know I can start handling the Bridges in my application, but this will make me change the dialplan flow and much more coding on my server application.
No, you can’t interact with ConfBridge using ARI. It is meant for writing YOUR OWN ConfBridge if you want or using bridges in other ways. User events are arbitrary events that you, the user, raise. There are no defined ones.
Hi Joshua,
Do you know if there is an alternative for the DialPlan function “WaitForSilence” using ARI?
Requirement:
I need to be notified in case there is a 3 second silence on a specific channel.
I believe the TALK_DETECT[1] dialplan function can be set on a channel in ARI and it will receive events. I would also suggest using other resources[2][3] instead of commenting on this unrelated blog post.
[1] https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Function_TALK_DETECT
[2] https://community.asterisk.org/
[3] http://lists.digium.com/cgi-bin/mailman/listinfo/asterisk-app-dev
Thanks Joshua and sorry for posting my question here. I believed it could be related. And the unrelated questions I addressed to the app-dev mailing list were never answered as well as in the community.
Hello Joshua!
I’ve just read your post about: Asterisk 14 ARI: Create, Bridge, Dial.
I am trying to set the caller id to appear on the customer’s phone but it keeps showing anonymous.
Checking the documentation, it says I can set it:
1) As a parameter
Query parameters => callerId: string – CallerID to use when dialing the endpoint or extension.
POST /ari/channels?endpoint=PJSIP/17162298050@clickproxytrunk&extension=s&context=ac_customer&callerId=12423424587&label=1
2) As a body variable
Body parameter
variables: containers – The “variables” key in the body object holds variable key/value pairs to set on the channel on creation. Other keys in the body object are interpreted as query parameters. Ex. { “endpoint”: “SIP/Alice”, “variables”: { “CALLERID(name)”: “Alice” } }
{“variables”:{“ac_phone_id”:”2914552″,”ac_client_id”:”24″,”ac_original_agent_id”:”24″,”ac_calllist_id”:”2090″,”ac_campaign_id”:”50″,”ac_account_id”:”18010965″,”ac_campaign_id”:”50″,”ac_automaticvoicemailstatus”:”2″,”ac_callrecording_path”:”/sc_recordings/client_24/2018-01-04/recording_outbound_10879″,”ac_action_id”:”5″,”ac_stasis_sufix”:”lucas”,”CALLERID(all)”:”666-6666″}}
But none of them worked.
I have not used this in quite some time. I would suggest using the community site, https://community.asterisk.org/ or the asterisk-app-dev mailing list for this question so others can participate and see the answer as well. You’ll also need to see whether it is going out to your VoIP upstream (pjsip set logger on) or not to isolate where the problem is.
try setting connectedline(num) on other channel
Hi Joshua, thanks for the post. I’m trying to achieve this simple task.
1. Create an outgoing channel
2. Set variables and callerid on the outgoing channel
3. Dial the outgoing channel
4. Play an audio once answered
Can you please let me know how this can be achieved?
If using ARI then you’d need to write an ARI application to do so. I don’t have an example that does exactly that, but the functionality needed to do it certainly exists.
ari.on(‘ChannelStateChange’, async function (event, outgoingChannel) {
const eventName = “ChannelStateChange”;
const { state } = event.channel;
if (state == “Ringing”) {
const currentTime = moment().format(‘YYYY-MM-DD-ddd h:mm:ss’);
const payload = { variable: “CDR(ring_time)”, value: currentTime }
await outgoingChannel.setChannelVar(payload).catch(err => log(“Error : unable to store ringing state on channel :”, outgoingChannel.id, err.message));
}
});
I’m facing an issue while setting CDR(“ring_time”) before answers
it throwing me an error “Channel is not in stasis application”.
Kindly help thanks.
Does early bridging functionality works in asterisk 18 , i am trying this in asterisk 18 version, but this not working, If there are any setting require then please suggest me.
this method is not creating cdr from both methods (cdr_csv and cdr _mysql)