Commit Β·
d15eabd
verified Β·
0
Parent(s):
initial commit
Browse files- .gitattributes +35 -0
- README.md +749 -0
.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
README.md
ADDED
|
@@ -0,0 +1,749 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: mit
|
| 3 |
+
tags:
|
| 4 |
+
- vicidial
|
| 5 |
+
- call-center
|
| 6 |
+
- asterisk
|
| 7 |
+
- ami
|
| 8 |
+
- commands
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
# Asterisk AMI Commands Guide
|
| 12 |
+
|
| 13 |
+
**Last updated: March 2026 | Reading time: ~26 minutes** The [Asterisk Manager Interface](/blog/asterisk-manager-interface-guide/) (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. --- 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]...
|
| 14 |
+
|
| 15 |
+
## Overview
|
| 16 |
+
|
| 17 |
+
**Last updated: March 2026 | Reading time: ~26 minutes**
|
| 18 |
+
|
| 19 |
+
The [Asterisk Manager Interface](/blog/asterisk-manager-interface-guide/) (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.
|
| 20 |
+
|
| 21 |
+
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.
|
| 22 |
+
|
| 23 |
+
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.
|
| 24 |
+
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
## Setting Up AMI
|
| 28 |
+
|
| 29 |
+
### manager.conf
|
| 30 |
+
|
| 31 |
+
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.
|
| 32 |
+
|
| 33 |
+
```ini
|
| 34 |
+
[general]
|
| 35 |
+
enabled = yes
|
| 36 |
+
port = 5038
|
| 37 |
+
bindaddr = 127.0.0.1
|
| 38 |
+
displayconnects = yes
|
| 39 |
+
timestampevents = yes
|
| 40 |
+
|
| 41 |
+
[cron]
|
| 42 |
+
secret = YOUR_SECRET_HERE
|
| 43 |
+
read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan,originate
|
| 44 |
+
write = system,call,agent,user,config,command,reporting,originate
|
| 45 |
+
|
| 46 |
+
[monitoring]
|
| 47 |
+
secret = ANOTHER_SECRET
|
| 48 |
+
read = system,call,agent,reporting,cdr
|
| 49 |
+
write = command
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
Key configuration points:
|
| 53 |
+
|
| 54 |
+
**`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.
|
| 55 |
+
|
| 56 |
+
**`read` permissions**: What events and data this user can receive.
|
| 57 |
+
**`write` permissions**: What actions this user can execute.
|
| 58 |
+
|
| 59 |
+
The permission categories:
|
| 60 |
+
| Permission | What It Controls |
|
| 61 |
+
|-----------|-----------------|
|
| 62 |
+
| `system` | System status, module management, shutdown |
|
| 63 |
+
| `call` | Channel events, call origination |
|
| 64 |
+
| `log` | Log events |
|
| 65 |
+
| `verbose` | Verbose messages |
|
| 66 |
+
| `agent` | Agent-related events and actions |
|
| 67 |
+
| `user` | User events |
|
| 68 |
+
| `config` | Configuration changes |
|
| 69 |
+
| `dtmf` | DTMF events and sending |
|
| 70 |
+
| `reporting` | CDR and reporting events |
|
| 71 |
+
| `cdr` | CDR data |
|
| 72 |
+
| `dialplan` | Dialplan events |
|
| 73 |
+
| `originate` | Call origination |
|
| 74 |
+
| `command` | CLI command execution through AMI |
|
| 75 |
+
|
| 76 |
+
After editing, reload:
|
| 77 |
+
|
| 78 |
+
```bash
|
| 79 |
+
asterisk -rx "manager reload"
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
### Testing Connectivity
|
| 83 |
+
|
| 84 |
+
The quickest way to test AMI:
|
| 85 |
+
|
| 86 |
+
```bash
|
| 87 |
+
telnet 127.0.0.1 5038
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
You'll see:
|
| 91 |
+
|
| 92 |
+
```
|
| 93 |
+
Asterisk Call Manager/6.0.0
|
| 94 |
+
```
|
| 95 |
+
|
| 96 |
+
Authenticate:
|
| 97 |
+
|
| 98 |
+
```
|
| 99 |
+
Action: Login
|
| 100 |
+
Username: cron
|
| 101 |
+
Secret: YOUR_SECRET_HERE
|
| 102 |
+
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
(Note the blank line after Secret β that's required. AMI uses blank lines as packet delimiters.)
|
| 106 |
+
|
| 107 |
+
Response:
|
| 108 |
+
|
| 109 |
+
```
|
| 110 |
+
Response: Success
|
| 111 |
+
Message: Authentication accepted
|
| 112 |
+
```
|
| 113 |
+
|
| 114 |
+
You're in. Now you can type actions directly.
|
| 115 |
+
|
| 116 |
+
---
|
| 117 |
+
|
| 118 |
+
## The Core AMI Actions
|
| 119 |
+
|
| 120 |
+
### 1. Originate β Make a Call
|
| 121 |
+
|
| 122 |
+
The most powerful AMI action. Tells Asterisk to originate a new call to a destination.
|
| 123 |
+
|
| 124 |
+
**Two modes:**
|
| 125 |
+
|
| 126 |
+
**Application mode** β Call a number and run a dialplan application:
|
| 127 |
+
|
| 128 |
+
```
|
| 129 |
+
Action: Originate
|
| 130 |
+
Channel: PJSIP/carrier/sip:3125551234@carrier.com
|
| 131 |
+
Context: from-internal
|
| 132 |
+
Exten: s
|
| 133 |
+
Priority: 1
|
| 134 |
+
CallerID: "Sales" <8005551234>
|
| 135 |
+
Timeout: 30000
|
| 136 |
+
Variable: campaign_id=SALES01
|
| 137 |
+
ActionID: orig-001
|
| 138 |
+
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
**Async mode** β Same but returns immediately instead of waiting for the call to connect:
|
| 142 |
+
|
| 143 |
+
```
|
| 144 |
+
Action: Originate
|
| 145 |
+
Channel: PJSIP/carrier/sip:3125551234@carrier.com
|
| 146 |
+
Application: MeetMe
|
| 147 |
+
Data: 8600100|qF
|
| 148 |
+
CallerID: "Sales" <8005551234>
|
| 149 |
+
Timeout: 30000
|
| 150 |
+
Async: true
|
| 151 |
+
ActionID: orig-002
|
| 152 |
+
|
| 153 |
+
```
|
| 154 |
+
|
| 155 |
+
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.
|
| 156 |
+
|
| 157 |
+
**Important parameters:**
|
| 158 |
+
- `Channel`: The outbound SIP channel (format depends on chan_sip vs PJSIP)
|
| 159 |
+
- `Context`/`Exten`/`Priority`: Where to send the call in the dialplan after it connects
|
| 160 |
+
- `Application`/`Data`: Alternative to Context β run a specific application directly
|
| 161 |
+
- `CallerID`: The caller ID to present
|
| 162 |
+
- `Timeout`: Ring timeout in milliseconds (30000 = 30 seconds)
|
| 163 |
+
- `Async`: Return immediately without waiting for call outcome
|
| 164 |
+
- `Variable`: Set channel variables (key=value, comma-separated for multiple)
|
| 165 |
+
- `ActionID`: Your identifier for matching the response
|
| 166 |
+
|
| 167 |
+
### 2. Redirect β Transfer a Call
|
| 168 |
+
|
| 169 |
+
Move an active channel to a different dialplan destination:
|
| 170 |
+
|
| 171 |
+
```
|
| 172 |
+
Action: Redirect
|
| 173 |
+
Channel: PJSIP/carrier-00000042
|
| 174 |
+
Context: transfer-dest
|
| 175 |
+
Exten: 3125559876
|
| 176 |
+
Priority: 1
|
| 177 |
+
ActionID: redir-001
|
| 178 |
+
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
For attended transfers (redirect two channels simultaneously):
|
| 182 |
+
|
| 183 |
+
```
|
| 184 |
+
Action: Redirect
|
| 185 |
+
Channel: PJSIP/carrier-00000042
|
| 186 |
+
ExtraChannel: PJSIP/agent-00000043
|
| 187 |
+
Context: transfer-dest
|
| 188 |
+
Exten: 3125559876
|
| 189 |
+
Priority: 1
|
| 190 |
+
ExtraContext: from-internal
|
| 191 |
+
ExtraExten: hangup
|
| 192 |
+
ExtraPriority: 1
|
| 193 |
+
ActionID: redir-002
|
| 194 |
+
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
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.
|
| 198 |
+
|
| 199 |
+
### 3. Hangup β End a Call
|
| 200 |
+
|
| 201 |
+
Terminate a specific channel:
|
| 202 |
+
|
| 203 |
+
```
|
| 204 |
+
Action: Hangup
|
| 205 |
+
Channel: PJSIP/carrier-00000042
|
| 206 |
+
Cause: 16
|
| 207 |
+
ActionID: hangup-001
|
| 208 |
+
|
| 209 |
+
```
|
| 210 |
+
|
| 211 |
+
Cause code 16 is "Normal clearing" β the standard hangup cause. Other common codes:
|
| 212 |
+
- 17 = User busy
|
| 213 |
+
- 19 = No answer
|
| 214 |
+
- 21 = Call rejected
|
| 215 |
+
- 31 = Normal, unspecified
|
| 216 |
+
|
| 217 |
+
### 4. Status β Query Active Channels
|
| 218 |
+
|
| 219 |
+
Get information about active channels:
|
| 220 |
+
|
| 221 |
+
```
|
| 222 |
+
Action: Status
|
| 223 |
+
ActionID: status-001
|
| 224 |
+
|
| 225 |
+
```
|
| 226 |
+
|
| 227 |
+
Returns a list of all active channels with their state, caller ID, duration, and bridged channel. For a specific channel:
|
| 228 |
+
|
| 229 |
+
```
|
| 230 |
+
Action: Status
|
| 231 |
+
Channel: PJSIP/carrier-00000042
|
| 232 |
+
ActionID: status-002
|
| 233 |
+
|
| 234 |
+
```
|
| 235 |
+
|
| 236 |
+
This is how monitoring systems check how many calls are active on the system at any moment.
|
| 237 |
+
|
| 238 |
+
### 5. CoreShowChannels β List All Channels
|
| 239 |
+
|
| 240 |
+
Similar to Status but returns a more detailed listing:
|
| 241 |
+
|
| 242 |
+
```
|
| 243 |
+
Action: CoreShowChannels
|
| 244 |
+
ActionID: channels-001
|
| 245 |
+
|
| 246 |
+
```
|
| 247 |
+
|
| 248 |
+
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.
|
| 249 |
+
|
| 250 |
+
### 6. GetVar β Read a Channel Variable
|
| 251 |
+
|
| 252 |
+
Read the value of a channel variable or dialplan function:
|
| 253 |
+
|
| 254 |
+
```
|
| 255 |
+
Action: GetVar
|
| 256 |
+
Channel: PJSIP/carrier-00000042
|
| 257 |
+
Variable: CALLERID(num)
|
| 258 |
+
ActionID: getvar-001
|
| 259 |
+
|
| 260 |
+
```
|
| 261 |
+
|
| 262 |
+
Response:
|
| 263 |
+
|
| 264 |
+
```
|
| 265 |
+
Response: Success
|
| 266 |
+
Variable: CALLERID(num)
|
| 267 |
+
Value: 3125551234
|
| 268 |
+
ActionID: getvar-001
|
| 269 |
+
```
|
| 270 |
+
|
| 271 |
+
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.
|
| 272 |
+
|
| 273 |
+
### 7. SetVar β Write a Channel Variable
|
| 274 |
+
|
| 275 |
+
Set a variable on an active channel:
|
| 276 |
+
|
| 277 |
+
```
|
| 278 |
+
Action: SetVar
|
| 279 |
+
Channel: PJSIP/carrier-00000042
|
| 280 |
+
Variable: CUSTOM_DATA
|
| 281 |
+
Value: priority_customer
|
| 282 |
+
ActionID: setvar-001
|
| 283 |
+
|
| 284 |
+
```
|
| 285 |
+
|
| 286 |
+
You can also set global variables (no Channel parameter):
|
| 287 |
+
|
| 288 |
+
```
|
| 289 |
+
Action: SetVar
|
| 290 |
+
Variable: GLOBAL_SETTING
|
| 291 |
+
Value: enabled
|
| 292 |
+
ActionID: setvar-002
|
| 293 |
+
|
| 294 |
+
```
|
| 295 |
+
|
| 296 |
+
### 8. Command β Execute CLI Commands
|
| 297 |
+
|
| 298 |
+
Run any Asterisk CLI command through AMI:
|
| 299 |
+
|
| 300 |
+
```
|
| 301 |
+
Action: Command
|
| 302 |
+
Command: core show channels
|
| 303 |
+
ActionID: cmd-001
|
| 304 |
+
|
| 305 |
+
```
|
| 306 |
+
|
| 307 |
+
Response includes the full CLI output. This is the escape hatch β anything you can type in `asterisk -r` you can execute through AMI.
|
| 308 |
+
|
| 309 |
+
Common commands:
|
| 310 |
+
- `sip show peers` / `pjsip show endpoints` β Check SIP registrations
|
| 311 |
+
- `core show channels` β Active channel count
|
| 312 |
+
- `queue show` β Queue statistics
|
| 313 |
+
- `database show` β AstDB contents
|
| 314 |
+
- `module reload res_pjsip.so` β Reload PJSIP without restarting
|
| 315 |
+
|
| 316 |
+
### 9. QueueStatus β Get Queue Information
|
| 317 |
+
|
| 318 |
+
Query the current state of Asterisk queues:
|
| 319 |
+
|
| 320 |
+
```
|
| 321 |
+
Action: QueueStatus
|
| 322 |
+
Queue: support-queue
|
| 323 |
+
ActionID: queue-001
|
| 324 |
+
|
| 325 |
+
```
|
| 326 |
+
|
| 327 |
+
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.
|
| 328 |
+
|
| 329 |
+
### 10. Monitor / StopMonitor β Call Recording
|
| 330 |
+
|
| 331 |
+
Start recording an active channel:
|
| 332 |
+
|
| 333 |
+
```
|
| 334 |
+
Action: Monitor
|
| 335 |
+
Channel: PJSIP/carrier-00000042
|
| 336 |
+
File: /var/spool/asterisk/monitor/20260326-call-042
|
| 337 |
+
Format: wav
|
| 338 |
+
Mix: true
|
| 339 |
+
ActionID: monitor-001
|
| 340 |
+
|
| 341 |
+
```
|
| 342 |
+
|
| 343 |
+
Stop recording:
|
| 344 |
+
|
| 345 |
+
```
|
| 346 |
+
Action: StopMonitor
|
| 347 |
+
Channel: PJSIP/carrier-00000042
|
| 348 |
+
ActionID: stopmon-001
|
| 349 |
+
|
| 350 |
+
```
|
| 351 |
+
|
| 352 |
+
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.
|
| 353 |
+
|
| 354 |
+
---
|
| 355 |
+
|
| 356 |
+
## AMI Events: Real-Time Call Tracking
|
| 357 |
+
|
| 358 |
+
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.
|
| 359 |
+
|
| 360 |
+
### Key Events
|
| 361 |
+
|
| 362 |
+
**Newchannel** β A new channel was created (call initiated):
|
| 363 |
+
|
| 364 |
+
```
|
| 365 |
+
Event: Newchannel
|
| 366 |
+
Channel: PJSIP/carrier-00000042
|
| 367 |
+
ChannelState: 0
|
| 368 |
+
ChannelStateDesc: Down
|
| 369 |
+
CallerIDNum: 3125551234
|
| 370 |
+
CallerIDName: John Smith
|
| 371 |
+
Uniqueid: 1711432800.42
|
| 372 |
+
```
|
| 373 |
+
|
| 374 |
+
**Hangup** β A channel was hung up:
|
| 375 |
+
|
| 376 |
+
```
|
| 377 |
+
Event: Hangup
|
| 378 |
+
Channel: PJSIP/carrier-00000042
|
| 379 |
+
Cause: 16
|
| 380 |
+
Cause-txt: Normal Clearing
|
| 381 |
+
Uniqueid: 1711432800.42
|
| 382 |
+
```
|
| 383 |
+
|
| 384 |
+
**BridgeEnter** / **BridgeLeave** β Channels joining/leaving bridges:
|
| 385 |
+
|
| 386 |
+
```
|
| 387 |
+
Event: BridgeEnter
|
| 388 |
+
Channel: PJSIP/carrier-00000042
|
| 389 |
+
BridgeUniqueid: bridge-001
|
| 390 |
+
BridgeType: basic
|
| 391 |
+
```
|
| 392 |
+
|
| 393 |
+
**AgentConnect** β An agent connected to a queue call:
|
| 394 |
+
|
| 395 |
+
```
|
| 396 |
+
Event: AgentConnect
|
| 397 |
+
Queue: sales-queue
|
| 398 |
+
Interface: PJSIP/agent001
|
| 399 |
+
MemberName: Agent Smith
|
| 400 |
+
HoldTime: 12
|
| 401 |
+
```
|
| 402 |
+
|
| 403 |
+
**DTMFReceived** β A DTMF digit was detected:
|
| 404 |
+
|
| 405 |
+
```
|
| 406 |
+
Event: DTMFReceived
|
| 407 |
+
Channel: PJSIP/carrier-00000042
|
| 408 |
+
Digit: 5
|
| 409 |
+
Direction: Received
|
| 410 |
+
```
|
| 411 |
+
|
| 412 |
+
### Filtering Events
|
| 413 |
+
|
| 414 |
+
On a busy system, AMI generates thousands of events per minute. Filter them:
|
| 415 |
+
|
| 416 |
+
```
|
| 417 |
+
Action: Events
|
| 418 |
+
EventMask: call,agent
|
| 419 |
+
ActionID: events-001
|
| 420 |
+
|
| 421 |
+
```
|
| 422 |
+
|
| 423 |
+
EventMask options: `on` (all events), `off` (no events), or a comma-separated list of event categories. This reduces noise and processing overhead.
|
| 424 |
+
|
| 425 |
+
---
|
| 426 |
+
|
| 427 |
+
## Practical AMI Automation Scripts
|
| 428 |
+
|
| 429 |
+
### Python: Click-to-Dial
|
| 430 |
+
|
| 431 |
+
A script that originates a call between an agent's phone and a customer number:
|
| 432 |
+
|
| 433 |
+
```python
|
| 434 |
+
import socket
|
| 435 |
+
|
| 436 |
+
class AMIClient:
|
| 437 |
+
def __init__(self, host, port, user, secret):
|
| 438 |
+
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
| 439 |
+
self.sock.connect((host, port))
|
| 440 |
+
self.sock.settimeout(5)
|
| 441 |
+
# Read banner
|
| 442 |
+
self.read_response()
|
| 443 |
+
# Login
|
| 444 |
+
self.send_action({
|
| 445 |
+
'Action': 'Login',
|
| 446 |
+
'Username': user,
|
| 447 |
+
'Secret': secret,
|
| 448 |
+
})
|
| 449 |
+
resp = self.read_response()
|
| 450 |
+
if 'Success' not in resp:
|
| 451 |
+
raise Exception(f"AMI login failed: {resp}")
|
| 452 |
+
|
| 453 |
+
def send_action(self, action):
|
| 454 |
+
msg = ''
|
| 455 |
+
for key, value in action.items():
|
| 456 |
+
msg += f'{key}: {value}\r\n'
|
| 457 |
+
msg += '\r\n'
|
| 458 |
+
self.sock.sendall(msg.encode())
|
| 459 |
+
|
| 460 |
+
def read_response(self):
|
| 461 |
+
data = b''
|
| 462 |
+
while True:
|
| 463 |
+
try:
|
| 464 |
+
chunk = self.sock.recv(4096)
|
| 465 |
+
data += chunk
|
| 466 |
+
if b'\r\n\r\n' in data:
|
| 467 |
+
break
|
| 468 |
+
except socket.timeout:
|
| 469 |
+
break
|
| 470 |
+
return data.decode()
|
| 471 |
+
|
| 472 |
+
def originate(self, channel, context, exten, callerid, variables=None):
|
| 473 |
+
action = {
|
| 474 |
+
'Action': 'Originate',
|
| 475 |
+
'Channel': channel,
|
| 476 |
+
'Context': context,
|
| 477 |
+
'Exten': exten,
|
| 478 |
+
'Priority': '1',
|
| 479 |
+
'CallerID': callerid,
|
| 480 |
+
'Timeout': '30000',
|
| 481 |
+
'Async': 'true',
|
| 482 |
+
}
|
| 483 |
+
if variables:
|
| 484 |
+
action['Variable'] = ','.join(f'{k}={v}' for k, v in variables.items())
|
| 485 |
+
self.send_action(action)
|
| 486 |
+
return self.read_response()
|
| 487 |
+
|
| 488 |
+
def hangup(self, channel):
|
| 489 |
+
self.send_action({
|
| 490 |
+
'Action': 'Hangup',
|
| 491 |
+
'Channel': channel,
|
| 492 |
+
'Cause': '16',
|
| 493 |
+
})
|
| 494 |
+
return self.read_response()
|
| 495 |
+
|
| 496 |
+
def close(self):
|
| 497 |
+
self.send_action({'Action': 'Logoff'})
|
| 498 |
+
self.sock.close()
|
| 499 |
+
|
| 500 |
+
# Usage: click-to-dial between agent extension and customer
|
| 501 |
+
ami = AMIClient('127.0.0.1', 5038, 'cron', 'YOUR_SECRET')
|
| 502 |
+
|
| 503 |
+
# First, call the agent's phone
|
| 504 |
+
result = ami.originate(
|
| 505 |
+
channel='PJSIP/agent001',
|
| 506 |
+
context='click-to-dial',
|
| 507 |
+
exten='3125551234',
|
| 508 |
+
callerid='"Click-to-Dial" <8005551234>',
|
| 509 |
+
variables={'customer_name': 'John Smith'}
|
| 510 |
+
)
|
| 511 |
+
print(f"Originate result: {result}")
|
| 512 |
+
ami.close()
|
| 513 |
+
```
|
| 514 |
+
|
| 515 |
+
### Node.js: Real-Time Event Monitor
|
| 516 |
+
|
| 517 |
+
A script that connects to AMI and streams call events:
|
| 518 |
+
|
| 519 |
+
```javascript
|
| 520 |
+
const net = require('net');
|
| 521 |
+
|
| 522 |
+
class AMIEventMonitor {
|
| 523 |
+
constructor(host, port, user, secret) {
|
| 524 |
+
this.client = new net.Socket();
|
| 525 |
+
this.buffer = '';
|
| 526 |
+
|
| 527 |
+
this.client.connect(port, host, () => {
|
| 528 |
+
console.log('Connected to AMI');
|
| 529 |
+
this.send(`Action: Login\r\nUsername: ${user}\r\nSecret: ${secret}\r\n\r\n`);
|
| 530 |
+
});
|
| 531 |
+
|
| 532 |
+
this.client.on('data', (data) => {
|
| 533 |
+
this.buffer += data.toString();
|
| 534 |
+
this.processBuffer();
|
| 535 |
+
});
|
| 536 |
+
|
| 537 |
+
this.client.on('error', (err) => {
|
| 538 |
+
console.error('AMI connection error:', err.message);
|
| 539 |
+
});
|
| 540 |
+
}
|
| 541 |
+
|
| 542 |
+
send(msg) {
|
| 543 |
+
this.client.write(msg);
|
| 544 |
+
}
|
| 545 |
+
|
| 546 |
+
processBuffer() {
|
| 547 |
+
const packets = this.buffer.split('\r\n\r\n');
|
| 548 |
+
this.buffer = packets.pop(); // Keep incomplete packet in buffer
|
| 549 |
+
|
| 550 |
+
for (const packet of packets) {
|
| 551 |
+
if (!packet.trim()) continue;
|
| 552 |
+
const event = {};
|
| 553 |
+
for (const line of packet.split('\r\n')) {
|
| 554 |
+
const idx = line.indexOf(': ');
|
| 555 |
+
if (idx > 0) {
|
| 556 |
+
event[line.substring(0, idx)] = line.substring(idx + 2);
|
| 557 |
+
}
|
| 558 |
+
}
|
| 559 |
+
this.handleEvent(event);
|
| 560 |
+
}
|
| 561 |
+
}
|
| 562 |
+
|
| 563 |
+
handleEvent(event) {
|
| 564 |
+
if (event.Event === 'Newchannel') {
|
| 565 |
+
console.log(`NEW CALL: ${event.CallerIDNum} -> Channel: ${event.Channel}`);
|
| 566 |
+
} else if (event.Event === 'Hangup') {
|
| 567 |
+
console.log(`HANGUP: ${event.Channel} (Cause: ${event['Cause-txt']})`);
|
| 568 |
+
} else if (event.Event === 'BridgeEnter') {
|
| 569 |
+
console.log(`BRIDGE: ${event.Channel} joined ${event.BridgeUniqueid}`);
|
| 570 |
+
} else if (event.Response === 'Success') {
|
| 571 |
+
console.log(`AUTH: ${event.Message}`);
|
| 572 |
+
// Filter to only call events
|
| 573 |
+
this.send('Action: Events\r\nEventMask: call\r\n\r\n');
|
| 574 |
+
}
|
| 575 |
+
}
|
| 576 |
+
}
|
| 577 |
+
|
| 578 |
+
const monitor = new AMIEventMonitor('127.0.0.1', 5038, 'monitoring', 'YOUR_SECRET');
|
| 579 |
+
```
|
| 580 |
+
|
| 581 |
+
### Bash: Quick Channel Count Check
|
| 582 |
+
|
| 583 |
+
A one-liner for monitoring scripts:
|
| 584 |
+
|
| 585 |
+
```bash
|
| 586 |
+
#!/bin/bash
|
| 587 |
+
# Count active channels via AMI
|
| 588 |
+
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}')
|
| 589 |
+
echo "Active channels: $CHANNELS"
|
| 590 |
+
```
|
| 591 |
+
|
| 592 |
+
---
|
| 593 |
+
|
| 594 |
+
## AMI Security
|
| 595 |
+
|
| 596 |
+
### Bind to Localhost
|
| 597 |
+
|
| 598 |
+
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:
|
| 599 |
+
|
| 600 |
+
```bash
|
| 601 |
+
ssh -L 5038:127.0.0.1:5038 user@asterisk-server
|
| 602 |
+
```
|
| 603 |
+
|
| 604 |
+
Then connect your client to `127.0.0.1:5038` on your local machine.
|
| 605 |
+
|
| 606 |
+
### Unique Secrets Per User
|
| 607 |
+
|
| 608 |
+
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.
|
| 609 |
+
|
| 610 |
+
### Minimal Permissions
|
| 611 |
+
|
| 612 |
+
Give each AMI user only the permissions they need:
|
| 613 |
+
- Monitoring system: `read=system,call` only
|
| 614 |
+
- Click-to-dial: `read=call`, `write=originate`
|
| 615 |
+
- VICIdial cron (needs everything): Full permissions
|
| 616 |
+
|
| 617 |
+
### Firewall
|
| 618 |
+
|
| 619 |
+
If AMI is bound to `0.0.0.0`, firewall it:
|
| 620 |
+
|
| 621 |
+
```bash
|
| 622 |
+
iptables -A INPUT -p tcp --dport 5038 -s 10.0.0.0/24 -j ACCEPT
|
| 623 |
+
iptables -A INPUT -p tcp --dport 5038 -j DROP
|
| 624 |
+
```
|
| 625 |
+
|
| 626 |
+
Only allow connections from known internal IPs.
|
| 627 |
+
|
| 628 |
+
### Audit Logging
|
| 629 |
+
|
| 630 |
+
Enable `displayconnects = yes` in manager.conf to log all AMI connections. Review these logs periodically.
|
| 631 |
+
|
| 632 |
+
---
|
| 633 |
+
|
| 634 |
+
## VICIdial-Specific AMI Patterns
|
| 635 |
+
|
| 636 |
+
VICIdial's backend scripts use AMI in specific patterns worth understanding:
|
| 637 |
+
|
| 638 |
+
### How VICIdial Dials Calls
|
| 639 |
+
|
| 640 |
+
The `VDauto_dial_CAMPAIGN.pl` cron script reads the [dial hopper](/blog/vicidial-dial-hopper-guide/), picks leads, and issues AMI Originate commands with Application: `MeetMe`. The call goes to a MeetMe conference room. When the call is answered (detected by answer supervision or AMD), VICIdial routes an available agent into the same MeetMe room via another AMI Originate.
|
| 641 |
+
|
| 642 |
+
### How Agent Monitoring Works
|
| 643 |
+
|
| 644 |
+
When a supervisor clicks "Listen" on the real-time report, VICIdial sends an AMI Originate to the supervisor's phone, connecting them to the same MeetMe conference room as the agent. The `q` flag on MeetMe means the supervisor enters in listen-only mode.
|
| 645 |
+
|
| 646 |
+
### How DTMF Injection Works
|
| 647 |
+
|
| 648 |
+
The agent screen's DTMF button triggers an AMI `AGI` action that runs `agi-dtmf.agi` on the customer channel. This script uses the `EXEC SendDTMF` command within the AGI session to inject digits into the correct call leg.
|
| 649 |
+
|
| 650 |
+
Understanding these patterns helps when troubleshooting VICIdial issues β many "VICIdial bugs" are AMI configuration or permission problems.
|
| 651 |
+
|
| 652 |
+
---
|
| 653 |
+
|
| 654 |
+
## When AMI Isn't Enough: ARI
|
| 655 |
+
|
| 656 |
+
Asterisk 12+ includes the Asterisk REST Interface (ARI), a more modern HTTP/WebSocket-based API. ARI gives you finer-grained control over individual channels, bridges, and recordings.
|
| 657 |
+
|
| 658 |
+
For new development, ARI is the recommended interface. AMI is still fully supported and VICIdial relies on it entirely, so you'll need AMI knowledge for VICIdial administration regardless.
|
| 659 |
+
|
| 660 |
+
For projects that need both VICIdial integration (AMI) and custom call control (ARI), they can coexist on the same Asterisk instance. Just be careful about conflicting channel manipulations β if VICIdial owns a channel through AMI and your ARI application tries to manipulate the same channel, things get unpredictable.
|
| 661 |
+
|
| 662 |
+
---
|
| 663 |
+
|
| 664 |
+
## Getting AMI Right
|
| 665 |
+
|
| 666 |
+
AMI is the power tool that makes Asterisk programmable and VICIdial possible. Most VICIdial admins never touch it directly because VICIdial abstracts it away. But when you need custom integrations, automated call flows, or advanced monitoring β AMI is where you go.
|
| 667 |
+
|
| 668 |
+
---
|
| 669 |
+
|
| 670 |
+
---
|
| 671 |
+
|
| 672 |
+
**Related reading:**
|
| 673 |
+
- [Asterisk Manager Interface (AMI): The Commands You'll Actually Use](https://vicistack.com/blog/asterisk-ami-commands-guide/)
|
| 674 |
+
- [Asterisk Manager Interface (AMI): The Complete Developer Guide](https://vicistack.com/blog/asterisk-manager-interface-guide/)
|
| 675 |
+
- [Asterisk PJSIP TLS Broken After OpenSSL 3 Upgrade? Here's the Fix for 'Wrong Curve' and Every Other Handshake Failure](https://vicistack.com/blog/asterisk-pjsip-tls-openssl3-guide/)
|
| 676 |
+
- [Asterisk Observability with OpenTelemetry and Grafana](https://vicistack.com/blog/asterisk-otel-observability/)
|
| 677 |
+
## AMI Troubleshooting
|
| 678 |
+
|
| 679 |
+
### "Connection Refused on Port 5038"
|
| 680 |
+
|
| 681 |
+
```bash
|
| 682 |
+
# Check if AMI is listening
|
| 683 |
+
ss -tlnp | grep 5038
|
| 684 |
+
```
|
| 685 |
+
|
| 686 |
+
If nothing shows, AMI isn't enabled. Check `manager.conf`:
|
| 687 |
+
- `enabled = yes` must be set in `[general]`
|
| 688 |
+
- `bindaddr` must match where you're connecting from
|
| 689 |
+
- Reload: `asterisk -rx "manager reload"`
|
| 690 |
+
|
| 691 |
+
### "Authentication Failed"
|
| 692 |
+
|
| 693 |
+
```bash
|
| 694 |
+
# Verify the user exists in manager.conf
|
| 695 |
+
asterisk -rx "manager show users"
|
| 696 |
+
```
|
| 697 |
+
|
| 698 |
+
Check that:
|
| 699 |
+
- Username and secret match exactly (case-sensitive)
|
| 700 |
+
- The user isn't disabled
|
| 701 |
+
- The connecting IP is allowed (check `permit`/`deny` ACLs in the user section)
|
| 702 |
+
|
| 703 |
+
### "Permission Denied" on Specific Actions
|
| 704 |
+
|
| 705 |
+
AMI permission errors mean the user's `write` permissions don't include the required category. For Originate, you need `write = originate`. For Command, you need `write = command`. For Redirect, you need `write = call`.
|
| 706 |
+
|
| 707 |
+
Check current permissions:
|
| 708 |
+
```bash
|
| 709 |
+
asterisk -rx "manager show user cron"
|
| 710 |
+
```
|
| 711 |
+
|
| 712 |
+
### Events Not Arriving
|
| 713 |
+
|
| 714 |
+
If you're connected and authenticated but not receiving events:
|
| 715 |
+
1. Check your EventMask β if set to `off`, no events will arrive
|
| 716 |
+
2. Verify `read` permissions include the event categories you expect
|
| 717 |
+
3. Check your socket buffer β if you're not reading fast enough, the TCP buffer fills and events are dropped
|
| 718 |
+
4. On busy systems (200+ concurrent calls), filter events aggressively with EventMask to avoid overwhelming your client
|
| 719 |
+
|
| 720 |
+
### AMI Performance at Scale
|
| 721 |
+
|
| 722 |
+
On a 200-agent VICIdial system, AMI handles thousands of actions per minute. Performance considerations:
|
| 723 |
+
|
| 724 |
+
- Each AMI connection consumes an Asterisk thread. Limit concurrent connections to 5-10
|
| 725 |
+
- Use a single persistent connection instead of connecting/disconnecting per action
|
| 726 |
+
- Filter events with EventMask β receiving all events on a busy system generates 50+ MB/hour of data
|
| 727 |
+
- Use Async:true for Originate actions to avoid blocking the AMI connection for 30+ seconds per call
|
| 728 |
+
|
| 729 |
+
### Common AMI Client Libraries
|
| 730 |
+
|
| 731 |
+
Rather than implementing raw TCP socket handling, use a library for your language:
|
| 732 |
+
|
| 733 |
+
**Python**: `panoramisk` or `starpy` β both handle connection management, authentication, and event parsing. Panoramisk supports async/await patterns for modern Python.
|
| 734 |
+
|
| 735 |
+
**Node.js**: `asterisk-manager` (npm) β provides an event emitter pattern that works well with Node's event loop.
|
| 736 |
+
|
| 737 |
+
**PHP**: `PAMI` (PHP [Asterisk Manager Interface](/blog/vicidial-custom-mysql-reports/)) β the most mature PHP AMI library.
|
| 738 |
+
|
| 739 |
+
**Go**: `ari-proxy` or `goami` β for high-performance AMI integrations.
|
| 740 |
+
|
| 741 |
+
Using a library eliminates the boilerplate of TCP connection management, packet parsing, and authentication handling. Focus on your business logic instead of protocol details.
|
| 742 |
+
|
| 743 |
+
If you're building AMI integrations for your call center and need the kind of deep Asterisk expertise that comes from hundreds of deployments, that's what [ViciStack](https://vicistack.com/) offers. We've built AMI-based automation for everything from click-to-dial CRM integrations to real-time quality monitoring dashboards. The $5K engagement covers your specific integration needs, not generic documentation.
|
| 744 |
+
|
| 745 |
+
## Resources
|
| 746 |
+
|
| 747 |
+
- [Read the full article](https://vicistack.com/blog/asterisk-ami-commands-guide/) on ViciStack
|
| 748 |
+
- [ViciStack](https://vicistack.com) - VICIdial hosting and optimization
|
| 749 |
+
- [Free VICIdial Audit](https://vicistack.com/free-audit/)
|