asterisk-rs-ari 0.8.0

Async Rust client for the Asterisk REST Interface (ARI)
Documentation

asterisk-rs-ari

crates.io docs.rs

Async Rust client for the Asterisk REST Interface (ARI). Build Stasis applications with full call control over REST + WebSocket.

Quick Start

use asterisk_rs_ari::{AriClient};
use asterisk_rs_ari::config::AriConfigBuilder;
use asterisk_rs_ari::event::AriEvent;
use asterisk_rs_ari::resources::channel::ChannelHandle;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    tracing_subscriber::fmt::init();

    let config = AriConfigBuilder::new("my-app")
        .host("10.0.0.1")
        .secure(true)
        .username("asterisk")
        .password("secret")
        .build()?;

    let client = AriClient::connect(config).await?;
    let mut events = client.subscribe();

    while let Some(msg) = events.recv().await {
        if let AriEvent::StasisStart { channel, .. } = msg.event {
            let handle = ChannelHandle::new(channel.id, client.clone());
            handle.answer().await?;
            handle.play("sound:hello-world").await?;
        }
    }

    Ok(())
}

Transport Modes

ARI supports two transport modes, selected at config time.

HTTP (default) — standard REST + WebSocket for events. Works with all supported Asterisk versions.

WebSocket — REST calls are tunnelled over the same WebSocket connection as events. Requires Asterisk 22+ with the unified WebSocket ARI transport enabled.

use asterisk_rs_ari::config::{AriConfigBuilder, TransportMode};

let config = AriConfigBuilder::new("my-app")
    .host("127.0.0.1")
    .username("asterisk")
    .password("asterisk")
    .transport(TransportMode::WebSocket)
    .build()?;

Race-Free Resource Creation

Subscribing to a channel's events after originating it creates a window where early events (including StasisStart) can be missed. PendingChannel eliminates that race by registering the event subscription before issuing the REST call.

use asterisk_rs_ari::resources::channel::OriginateParams;

let pending = client.channel();
let params = OriginateParams::new("PJSIP/100").app("my-app");
let (handle, mut events) = pending.originate(params).await?;
// events are buffered from creation — StasisStart is guaranteed captured

while let Some(msg) = events.recv().await {
    // handle per-channel events
}

The same pattern applies to bridges (client.bridge()) and playbacks (client.playback()).

Resource Handles

Handles wrap a resource ID and a cloned client reference, exposing typed methods without requiring you to construct REST paths manually.

ChannelHandle

  • answer(), hangup(reason)
  • play(media_uri), record(name, format, ...)
  • mute(direction), unmute(direction)
  • hold(), unhold()
  • send_dtmf(digit, ...)
  • get_variable(name), set_variable(name, value)

BridgeHandle

  • add_channel(channel_id), remove_channel(channel_id)
  • play(media_uri)
  • start_moh(class), stop_moh()
  • destroy()

PlaybackHandle

  • pause(), unpause(), restart(), stop()

RecordingHandle

  • stop(), pause(), unpause(), mute(), unmute(), get()

Outbound WebSocket Server

For Asterisk 22+ deployments, ARI can connect outbound to your application instead of the reverse. AriServer listens for those incoming Asterisk connections.

use asterisk_rs_ari::server::AriServer;

let (server, shutdown) = AriServer::builder()
    .bind("0.0.0.0:8765")
    .allow_external_bind(true)
    .build().await?;

server.run(|session| async move {
    let mut events = session.subscribe();
    while let Some(msg) = events.recv().await {
        tracing::info!(event = ?msg.event, "received");
    }
}).await?;

Media Channel

MediaChannel provides raw audio exchange through chan_websocket's JSON control protocol. Asterisk defaults chan_websocket to plaintext, so create the external media channel with ExternalMediaParams::websocket_json; it selects encapsulation=none, transport=websocket, and transport_data=f(json).

use asterisk_rs_ari::media::MediaChannel;
use asterisk_rs_ari::resources::channel::ExternalMediaParams;

let params = ExternalMediaParams::websocket_json(
    "my-stasis-app",
    "connection-id",
    "ulaw",
);
let channel = asterisk_rs_ari::resources::channel::external_media(&ari, &params).await?;

let media = MediaChannel::connect("wss://asterisk:8089/media/connection-id").await?;
media.answer().await?;

while let Some(audio) = media.recv_audio().await? {
    // process or echo audio bytes
    media.send_audio(audio).await?;
}

Cleartext http:///ws:// is accepted by default only for loopback hosts. Remote cleartext requires the explicit allow_insecure_remote(true) opt-in. For private PKI, pass the PEM CA bundle with AriConfigBuilder::private_ca_pem; the same parsed roots are applied to HTTPS, event WSS, and unified WSS. Use MediaConnectionOptions::private_ca_pem for a media WSS connection.

Capabilities

  • REST/WebSocket clients for the modeled ARI surface, with Asterisk 22 as the live-proven boundary
  • Typed events with metadata (application, timestamp, asterisk_id) on every event
  • Filtered subscriptions -- receive only events you care about
  • Race-free resource creation with PendingChannel, PendingBridge, PendingPlayback
  • Resource handles for channels, bridges, playbacks, recordings
  • Dual transport modes: HTTP and unified WebSocket (Asterisk 22+)
  • Outbound WebSocket server for Asterisk 22+ outbound connections
  • Media channel for raw audio exchange via chan_websocket
  • System management -- modules, logging, config, global variables
  • URL-safe query encoding for user-provided values
  • HTTP connect and request timeouts
  • Automatic WebSocket reconnection
  • #[non_exhaustive] enums -- new variants won't break your code

Documentation

Part of asterisk-rs. MSRV 1.86. MIT/Apache-2.0.