Node.js ARI Client -- Channel Events

Integrations -- Last reviewed 2026-08-22 ari nodejs integrations javascript stasis Found this useful? Upvote it. ×

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

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

  1. 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.
  2. 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. The channel object provides methods like ring(), answer(), hangup(), play(), and startSilence().
  3. StasisEnd: Fires when the channel leaves Stasis (hung up, or Stasis() exits). Clean up timers and resources here to avoid memory leaks.
  4. ChannelStateChange: Fires on state transitions (Ring, Up, Down, etc.). Useful for logging and for driving UI updates in real-time applications.
  5. 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 →.
  6. Timer cleanup: The timers map tracks pending setTimeout handles per channel ID. On StasisEnd, any pending timer is cleared so the app doesn't attempt to operate on a channel that no longer exists.

Tips

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.

Moderated before publishing. Email never shown.
Related Snippets