What ARI is
The Asterisk REST Interface is an asynchronous API for building communications applications. Rather than describing a call flow in dialplan syntax, ARI exposes the raw primitive objects inside Asterisk — channels, bridges, endpoints, media — through a REST interface, and lets your application manipulate them directly.
That difference in level is the point. The dialplan is a scripting language for telephony; ARI hands you the objects themselves and gets out of the way. If you are building an IVR with real logic, a voicebot, or anything that must make decisions mid-call based on external data, ARI is the right tool and the dialplan is not.
Stasis: how a channel gets handed over
The bridge between the two worlds is Stasis — a dialplan application whose entire job is handing control of a channel from the dialplan over to ARI and your client.
exten => 300,1,Answer()
same => n,Stasis(my-app)
same => n,Hangup()When a channel enters a Stasis application, Asterisk checks whether a WebSocket connection has been established for that application name. If one has, the channel is handed to ARI for control, a subscription is created for that channel on the WebSocket, and a StasisStart event is sent to your client.
From that moment the dialplan is not driving the call — your application is. When you are finished you either hang up or send the channel back to the dialplan to continue.
The implication worth internalising: if no WebSocket is connected for that application name, there is nobody to take the channel. Your application must be connected and listening before calls arrive, which makes reconnection logic a first-class concern rather than an afterthought.
The two halves: REST in, events out
ARI is deliberately split.
You send commands over REST. Answer a channel, play a file, create a bridge, add a channel to it, record — each is an HTTP request against the relevant resource.
Asterisk sends events over a WebSocket, connected at /ari/events, with events delivered as JSON messages. These tell your application about state changes — including ones caused by your own REST calls, since the API is asynchronous.
That asynchrony is the mental adjustment. A REST call to start a playback returns immediately; the playback finishing arrives later as an event. You are writing an event-driven application, not a linear script, and code structured as "do this, then that" will not work.
Enable the interface in ari.conf:
[general]
enabled = yes
pretty = yes
[my-app]
type = user
password = a-long-random-secretARI is served by Asterisk's HTTP daemon, so http.conf must have it enabled and bound appropriately. As with AMI, bind to localhost unless you have a specific reason not to.
The gotcha: bridges are not auto-subscribed
Here is the detail that catches essentially everyone the first time, because it is inconsistent with how channels behave.
When a channel enters Stasis, your application is automatically subscribed to it and receives its events. Reasonable, and easy to assume it generalises.
It does not. Bridges in a Stasis application are not automatically subscribed to. Create a bridge, add channels to it, and you will receive no bridge events at all — your application appears to work until you need to know something about the bridge, at which point it is silently deaf.
To receive them you must subscribe explicitly, using the applications resource:
POST /applications/{app_name}/subscriptionIf you are building a conference or any multi-party feature and bridge events never arrive, this is why.
A typical flow
A simple IVR in ARI terms:
- Channel hits
Stasis(my-app); you receiveStasisStart. POST /channels/{id}/answer.POST /channels/{id}/playwith the media URI for your prompt.- Wait for
PlaybackFinished, then forChannelDtmfReceivedevents as the caller presses keys. - Decide what to do — query your CRM, look up an account, whatever the logic requires.
- Create a bridge, add the caller and an agent channel, and let them talk.
- On
StasisEndor hangup, clean up.
Step 5 is the entire justification for using ARI. Making an HTTP call to your own systems mid-call and branching on the result is awkward in the dialplan and natural here.
Playback: to a channel or to a bridge
Media can be played to an individual channel or to a whole bridge, and the distinction is genuinely useful. Announcing "this call is being recorded" belongs to the bridge so everyone hears it; telling one caller "you have joined the conference" belongs to that channel alone.
Getting this wrong is a common source of odd conference behaviour — a per-participant prompt played to the bridge interrupts everyone.
ARI or AMI?
- AMI observes and issues coarse commands: originate a call, watch events, drive a wallboard. It was not designed to hold a call and reason about it.
- ARI takes control of channels and manipulates them directly. Use it for IVRs, voicebots, conferencing and anything with real logic.
For AI voice agents specifically, ARI is the natural foundation: you control the media, can stream audio in and out, and can make decisions per utterance — none of which the dialplan can express.
Frequently asked questions
What is Stasis?
A dialplan application that hands control of a channel from the dialplan to ARI. Sending a call to Stasis(app-name) is how it enters your application.
Why is my application not receiving channels?
The WebSocket must be connected for that application name before the channel enters Stasis. If nothing is listening, there is no client to hand the channel to.
Why do I get no bridge events?
Bridges are not subscribed to automatically the way channels are. Subscribe explicitly via POST /applications/{app_name}/subscription.
Should I use ARI or the dialplan?
The dialplan for routing and standard telephony features. ARI when call flow depends on external data or logic that would be painful to express in dialplan syntax.