npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vicistack/asterisk-ami-commands-guide

v1.0.0

Published

Asterisk AMI Commands Guide — ViciStack call center engineering guide

Downloads

46

Readme

Asterisk AMI Commands Guide

Last updated: March 2026 | Reading time: ~26 minutes The Asterisk Manager Interface (AMI) is the programmatic backdoor to your PBX. It's a TCP socket protocol that lets external programs control Asterisk — originate calls, redirect channels, read variables, monitor events, and do anything the Asterisk CLI can do, but from code. VICIdial uses AMI extensively under the hood. The dialer cron scripts originate calls through AMI. The agent screen sends DTMF through AMI. Call monitoring, conference management, and agent session control all go through AMI. If you understand AMI, you understand the engine that drives VICIdial's real-time operations. The official documentation lists 150+ commands. You'll use maybe 15 of them regularly. Here are those 15, with the exact syntax and practical examples. --- ## Setting Up AMI ### manager.conf AMI is configured in /etc/asterisk/manager.conf. VICIbox ships with this pre-configured for VICIdial, but understanding the config helps when you need to add custom integrations. ini [general] enabled = yes port = 5038 bindaddr = 127.0.0.1 displayconnects = yes timestampevents = yes [cron] secret = YOUR_SECRET_HERE read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan,originate write = system,call,agent,user,config,command,reporting,originate [monitoring] secret = ANOTHER_SECRET read = system,call,agent,reporting,cdr write = command Key configuration points: bindaddr = 127.0.0.1: Binds AMI to localhost only. This means only programs running on the same server can connect. This is the safe default. If you need remote AMI access (for external dashboards or integrations), change this to 0.0.0.0 and add firewall rules. read permissions: What events and data this user can receive. write permissions: What actions this user can execute. The permission categories: | Permission | What It Controls | |-----------|-----------------| | system | System status, module management, shutdown | | call | Channel events, call origination | | log | Log events | | verbose | Verbose messages | | agent | Agent-related events and actions | | user | User events | | config | Configuration changes | | dtmf | DTMF events and sending | | reporting | CDR and reporting events | | cdr | CDR data | | dialplan | Dialplan events | | originate | Call origination | | command | CLI command execution through AMI | After editing, reload: bash asterisk -rx "manager reload" ### Testing Connectivity The quickest way to test AMI: bash telnet 127.0.0.1 5038 You'll see: Asterisk Call Manager/6.0.0 Authenticate: Action: Login Username: cron Secret: YOUR_SECRET_HERE (Note the blank line after Secret — that's required. AMI uses blank lines as packet delimiters.) Response: Response: Success Message: Authentication accepted You're in. Now you can type actions directly. --- ## The Core AMI Actions ### 1. Originate — Make a Call The most powerful AMI action. Tells Asterisk to originate a new call to a destination. Two modes: Application mode — Call a number and run a dialplan application: Action: Originate Channel: PJSIP/carrier/sip:[email protected] Context: from-internal Exten: s Priority: 1 CallerID: "Sales" <8005551234> Timeout: 30000 Variable: campaign_id=SALES01 ActionID: orig-001 Async mode — Same but returns immediately instead of waiting for the call to connect: Action: Originate Channel: PJSIP/carrier/sip:[email protected] Application: MeetMe Data: 8600100|qF CallerID: "Sales" <8005551234> Timeout: 30000 Async: true ActionID: orig-002 VICIdial uses Application mode with MeetMe to bridge calls into conference rooms where agents are already waiting. That's how the predictive dialer works — it originates a call, and when it connects, the call lands in a MeetMe room where an agent is listening. Important parameters: - Channel: The outbound SIP channel (format depends on chan_sip vs PJSIP) - Context/Exten/Priority: Where to send the call in the dialplan after it connects - Application/Data: Alternative to Context — run a specific application directly - CallerID: The caller ID to present - Timeout: Ring timeout in milliseconds (30000 = 30 seconds) - Async: Return immediately without waiting for call outcome - Variable: Set channel variables (key=value, comma-separated for multiple) - ActionID: Your identifier for matching the response ### 2. Redirect — Transfer a Call Move an active channel to a different dialplan destination: Action: Redirect Channel: PJSIP/carrier-00000042 Context: transfer-dest Exten: 3125559876 Priority: 1 ActionID: redir-001 For attended transfers (redirect two channels simultaneously): Action: Redirect Channel: PJSIP/carrier-00000042 ExtraChannel: PJSIP/agent-00000043 Context: transfer-dest Exten: 3125559876 Priority: 1 ExtraContext: from-internal ExtraExten: hangup ExtraPriority: 1 ActionID: redir-002 VICIdial uses Redirect for blind transfers, warm transfers, and parking. When an agent clicks "Transfer" on the agent screen, the web interface sends an AMI Redirect action to move the call. ### 3. Hangup — End a Call Terminate a specific channel: Action: Hangup Channel: PJSIP/carrier-00000042 Cause: 16 ActionID: hangup-001 Cause code 16 is "Normal clearing" — the standard hangup cause. Other common codes: - 17 = User busy - 19 = No answer - 21 = Call rejected - 31 = Normal, unspecified ### 4. Status — Query Active Channels Get information about active channels: Action: Status ActionID: status-001 Returns a list of all active channels with their state, caller ID, duration, and bridged channel. For a specific channel: Action: Status Channel: PJSIP/carrier-00000042 ActionID: status-002 This is how monitoring systems check how many calls are active on the system at any moment. ### 5. CoreShowChannels — List All Channels Similar to Status but returns a more detailed listing: Action: CoreShowChannels ActionID: channels-001 Response includes channel name, context, extension, priority, state, application, duration, caller ID, and account code for every active channel. Useful for building real-time call dashboards. ### 6. GetVar — Read a Channel Variable Read the value of a channel variable or dialplan function: Action: GetVar Channel: PJSIP/carrier-00000042 Variable: CALLERID(num) ActionID: getvar-001 Response: Response: Success Variable: CALLERID(num) Value: 3125551234 ActionID: getvar-001 You can read any channel variable or dialplan function this way. Useful for checking call state, reading custom data attached to calls, and debugging dialplan logic. ### 7. SetVar — Write a Channel Variable Set a variable on an active channel: Action: SetVar Channel: PJSIP/carrier-00000042 Variable: CUSTOM_DATA Value: priority_customer ActionID: setvar-001 You can also set global variables (no Channel parameter): Action: SetVar Variable: GLOBAL_SETTING Value: enabled ActionID: setvar-002 ### 8. Command — Execute CLI Commands Run any Asterisk CLI command through AMI: Action: Command Command: core show channels ActionID: cmd-001 Response includes the full CLI output. This is the escape hatch — anything you can type in asterisk -r you can execute through AMI. Common commands: - sip show peers / pjsip show endpoints — Check SIP registrations - core show channels — Active channel count - queue show — Queue statistics - database show — AstDB contents - module reload res_pjsip.so — Reload PJSIP without restarting ### 9. QueueStatus — Get Queue Information Query the current state of Asterisk queues: Action: QueueStatus Queue: support-queue ActionID: queue-001 Returns queue members, their status (available/paused/busy), and current callers waiting. For VICIdial, queue data is managed differently (through the VICIdial application layer), but this is useful for hybrid setups that use both VICIdial and native Asterisk queues. ### 10. Monitor / StopMonitor — Call Recording Start recording an active channel: Action: Monitor Channel: PJSIP/carrier-00000042 File: /var/spool/asterisk/monitor/20260326-call-042 Format: wav Mix: true ActionID: monitor-001 Stop recording: Action: StopMonitor Channel: PJSIP/carrier-00000042 ActionID: stopmon-001 VICIdial handles recording through its own mechanism (campaign-level recording settings), but Monitor/StopMonitor is useful for on-demand recording of specific calls from external systems. --- ## AMI Events: Real-Time Call Tracking AMI isn't just for sending commands — it's also a real-time event stream. When you connect and authenticate, Asterisk pushes events to your connection as they happen. ### Key Events Newchannel — A new channel was created (call initiated): Event: Newchannel Channel: PJSIP/carrier-00000042 ChannelState: 0 ChannelStateDesc: Down CallerIDNum: 3125551234 CallerIDName: John Smith Uniqueid: 1711432800.42 Hangup — A channel was hung up: Event: Hangup Channel: PJSIP/carrier-00000042 Cause: 16 Cause-txt: Normal Clearing Uniqueid: 1711432800.42 BridgeEnter / BridgeLeave — Channels joining/leaving bridges: Event: BridgeEnter Channel: PJSIP/carrier-00000042 BridgeUniqueid: bridge-001 BridgeType: basic AgentConnect — An agent connected to a queue call: Event: AgentConnect Queue: sales-queue Interface: PJSIP/agent001 MemberName: Agent Smith HoldTime: 12 DTMFReceived — A DTMF digit was detected: Event: DTMFReceived Channel: PJSIP/carrier-00000042 Digit: 5 Direction: Received ### Filtering Events On a busy system, AMI generates thousands of events per minute. Filter them: Action: Events EventMask: call,agent ActionID: events-001 EventMask options: on (all events), off (no events), or a comma-separated list of event categories. This reduces noise and processing overhead. --- ## Practical AMI Automation Scripts ### Python: Click-to-Dial A script that originates a call between an agent's phone and a customer number: python import socket class AMIClient: def __init__(self, host, port, user, secret): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((host, port)) self.sock.settimeout(5) # Read banner self.read_response() # Login self.send_action({ 'Action': 'Login', 'Username': user, 'Secret': secret, }) resp = self.read_response() if 'Success' not in resp: raise Exception(f"AMI login failed: {resp}") def send_action(self, action): msg = '' for key, value in action.items(): msg += f'{key}: {value}\r\n' msg += '\r\n' self.sock.sendall(msg.encode()) def read_response(self): data = b'' while True: try: chunk = self.sock.recv(4096) data += chunk if b'\r\n\r\n' in data: break except socket.timeout: break return data.decode() def originate(self, channel, context, exten, callerid, variables=None): action = { 'Action': 'Originate', 'Channel': channel, 'Context': context, 'Exten': exten, 'Priority': '1', 'CallerID': callerid, 'Timeout': '30000', 'Async': 'true', } if variables: action['Variable'] = ','.join(f'{k}={v}' for k, v in variables.items()) self.send_action(action) return self.read_response() def hangup(self, channel): self.send_action({ 'Action': 'Hangup', 'Channel': channel, 'Cause': '16', }) return self.read_response() def close(self): self.send_action({'Action': 'Logoff'}) self.sock.close() # Usage: click-to-dial between agent extension and customer ami = AMIClient('127.0.0.1', 5038, 'cron', 'YOUR_SECRET') # First, call the agent's phone result = ami.originate( channel='PJSIP/agent001', context='click-to-dial', exten='3125551234', callerid='"Click-to-Dial" <8005551234>', variables={'customer_name': 'John Smith'} ) print(f"Originate result: {result}") ami.close() ### Node.js: Real-Time Event Monitor A script that connects to AMI and streams call events: javascript const net = require('net'); class AMIEventMonitor { constructor(host, port, user, secret) { this.client = new net.Socket(); this.buffer = ''; this.client.connect(port, host, () => { console.log('Connected to AMI'); this.send(`Action: Login\r\nUsername: ${user}\r\nSecret: ${secret}\r\n\r\n`); }); this.client.on('data', (data) => { this.buffer += data.toString(); this.processBuffer(); }); this.client.on('error', (err) => { console.error('AMI connection error:', err.message); }); } send(msg) { this.client.write(msg); } processBuffer() { const packets = this.buffer.split('\r\n\r\n'); this.buffer = packets.pop(); // Keep incomplete packet in buffer for (const packet of packets) { if (!packet.trim()) continue; const event = {}; for (const line of packet.split('\r\n')) { const idx = line.indexOf(': '); if (idx > 0) { event[line.substring(0, idx)] = line.substring(idx + 2); } } this.handleEvent(event); } } handleEvent(event) { if (event.Event === 'Newchannel') { console.log(`NEW CALL: ${event.CallerIDNum} -> Channel: ${event.Channel}`); } else if (event.Event === 'Hangup') { console.log(`HANGUP: ${event.Channel} (Cause: ${event['Cause-txt']})`); } else if (event.Event === 'BridgeEnter') { console.log(`BRIDGE: ${event.Channel} joined ${event.BridgeUniqueid}`); } else if (event.Response === 'Success') { console.log(`AUTH: ${event.Message}`); // Filter to only call events this.send('Action: Events\r\nEventMask: call\r\n\r\n'); } } } const monitor = new AMIEventMonitor('127.0.0.1', 5038, 'monitoring', 'YOUR_SECRET'); ### Bash: Quick Channel Count Check A one-liner for monitoring scripts: bash #!/bin/bash # Count active channels via AMI CHANNELS=$(echo -e "Action: Login\r\nUsername: cron\r\nSecret: YOUR_SECRET\r\n\r\nAction: CoreShowChannels\r\n\r\nAction: Logoff\r\n\r\n" | nc -w 3 127.0.0.1 5038 | grep "ListItems:" | awk '{print $2}') echo "Active channels: $CHANNELS" --- ## AMI Security ### Bind to Localhost Unless you have a specific need for remote AMI access, keep bindaddr = 127.0.0.1. If you need remote access, use SSH tunneling instead: bash ssh -L 5038:127.0.0.1:5038 user@asterisk-server Then connect your client to 127.0.0.1:5038 on your local machine. ### Unique Secrets Per User Every AMI user should have a different secret. If you use the same secret for all users, you can't revoke one user's access without changing everyone's credentials. ### Minimal Permissions Give each AMI user only the permissions they need: - Monitoring system: read=system,call only - Click-to-dial: read=call, write=originate - VICIdial cron (needs everything): Full permissions ### Firewall If AMI is bound to 0.0.0.0, firewall it: bash iptables -A INPUT -p tcp --dport 5038 -s 10.0.0.0/24 -j ACCEPT iptables -A INPUT -p tcp --dport 5038 -j DROP Only allow connections from known internal IPs.


Read the full article

About

Built by ViciStack — enterprise VoIP and call center infrastructure.

License

MIT