Node.js ARI Client -- Channel Events
Node.js ARI Client: Channel Events
A minimal Node.js application that connects to the Asterisk REST Interface, listens for channelA single call leg passing through Asterisk. Channels represent connections to endpoints and are what dialplan applications act on. events, and controls the call lifecycle: ring the channel, answer after 2 seconds, play silence, then hang up after 4 seconds.
Requirements
- ARIAsterisk REST Interface. A modern asynchronous API exposing channels, bridges, and endpoints over HTTP and WebSocket for building custom call applications. enabled in Asterisk (see ARI Setup and Stasis Application)
- Node.js installed on the Asterisk server or a host that can reach port 8088
ari-clientnpm package
package.json
{
"name": "nodejs-channel-state",
"version": "1.0.0",
"main": "channel-state.js",
"dependencies": {
"ari-client": "^2.2.0"
}
}
channel-state.js
'use strict';
var ari = require('ari-client');
var util = require('util');
var timers = {};
ari.connect('http://localhost:8088', 'asterisk', 'asterisk', clientLoaded);
function clientLoaded(err, client) {
if (err) { throw err; }
function stasisStart(event, channel) {
console.log(util.format(
'Channel %s has entered the application', channel.name));
// Ring the channel immediately
channel.ring(function(err) { if (err) throw err; });
// Answer after 2 seconds
var timer = setTimeout(function() {
console.log(util.format('Answering channel %s', channel.name));
channel.answer(function(err) { if (err) throw err; });
channel.startSilence(function(err) { if (err) throw err; });
// Hang up after 4 more seconds
timers[channel.id] = setTimeout(function() {
console.log(util.format('Hanging up channel %s', channel.name));
channel.hangup(function(err) { if (err) throw err; });
}, 4000);
}, 2000);
timers[channel.id] = timer;
}
function stasisEnd(event, channel) {
console.log(util.format(
'Channel %s just left our application', channel.name));
var timer = timers[channel.id];
if (timer) {
clearTimeout(timer);
delete timers[channel.id];
}
}
function channelStateChange(event, channel) {
console.log(util.format(
'Channel %s is now: %s', channel.name, channel.state));
}
client.on('StasisStart', stasisStart);
client.on('StasisEnd', stasisEnd);
client.on('ChannelStateChange', channelStateChange);
client.start('channel-state');
}
Dialplan
[from-trunk]
exten => 1000,1,NoOp(Sending to ARI channel-state app)
same => n,Stasis(channel-state)
same => n,Hangup()
How it works
- ari.connect(): Establishes a WebSocket connection to Asterisk's ARI endpointIn PJSIP configuration, the SIP entity Asterisk communicates with: a phone, trunk, or other user agent. Defined by an endpoint section in pjsip.conf.. The three arguments after the URL are the ARI username and password from
ari.conf. - StasisStart: Fires when a channel enters the
Stasis()dialplanThe core call-routing configuration of Asterisk, written mostly in extensions.conf as contexts, extensions, and priorities that decide how every call is handled. Full definition → application. This is the entry point for your application logic. Thechannelobject provides methods likering(),answer(),hangup(),play(), andstartSilence(). - StasisEnd: Fires when the channel leaves Stasis (hung up, or
Stasis()exits). Clean up timers and resources here to avoid memory leaks. - ChannelStateChange: Fires on state transitions (Ring, Up, Down, etc.). Useful for logging and for driving UI updates in real-time applications.
- client.start('channel-state'): Subscribes to events for the named Stasis application. The name must match the argument passed to
Stasis()in the dialplanThe core call-routing configuration of Asterisk, written mostly in extensions.conf as contexts, extensions, and priorities that decide how every call is handled. Full definition →. - Timer cleanup: The
timersmap tracks pendingsetTimeouthandles per channel ID. OnStasisEnd, any pending timer is cleared so the app doesn't attempt to operate on a channel that no longer exists.
Tips
- Install and run:
npm install && node channel-state.js. - Replace
channel.startSilence()withchannel.play({media: 'sound:hello-world'})to play a sound file instead of silence. - For production, use the
async/awaitAPI (ari-clientsupports promises) instead of callback-style error handling. - Bridge two channels together with
client.bridges.create()andbridge.addChannel()to build a simple call-routing application. - Monitor the WebSocket connection for disconnects and implement automatic reconnection logic for reliability.
User Notes
Know a tip or gotcha for this topic? Share it below and help others.
Contribute a note
Share a tip, gotcha, or practical example. Keep it under 2000 characters. No questions (use the Asterisk community forums for support). Wrap code in backticks.