AMI / AJAM
Interacting with Asterisk via AMI (TCP 5038) and AJAM (HTTP).
MikoPBX talks to Asterisk through two manager interfaces:
AMI (Asterisk Manager Interface) — a line-oriented TCP protocol on port 5038. This is the primary channel used by core workers and modules to send commands (
Originate,Hangup,Redirect, …) and to subscribe to events (Newchannel,Hangup,UserEvent, …).AJAM (Asterisk JavaScript Asynchronous Manager) — the same manager actions, exposed over HTTP (default port 8088, TLS 8089) under the
/asterisk/path prefix. Convenient for shell scripts, web clients, and ad-hoc testing withcurl.
From PHP you almost never open the socket yourself. You ask the DI container for a ready, authenticated AsteriskManager instance. Raw protocol (telnet / curl) is useful for debugging and for one-off operations from the PBX shell.
AsteriskManager: the AMI client
The AMI client is MikoPBX\Core\Asterisk\AsteriskManager.
namespace MikoPBX\Core\Asterisk;
class AsteriskManager
{
public function connect(
?string $server = null,
?string $username = null,
?string $secret = null,
string $events = 'on'
): bool;
public function disconnect(): void;
public function loggedIn(): bool;
}The constructor accepts an optional path to a config file (parsed with parse_ini_file) and an $optconfig array. When values are missing it falls back to defaults: server=localhost, port=5038, username=phpagi, secret=phpagi. connect() accepts a server:port string (e.g. 127.0.0.1:5038), checks that the Asterisk process is actually listening, opens the socket with a 2-second timeout, and logs in. On a successful Login response loggedIn() returns true.
Getting an instance from the DI container
Do not instantiate AsteriskManager directly in module code. Use the helper MikoPBX\Core\System\Util::getAstManager(), which returns a shared, already-connected instance from the DI container.
getAstManager() maps the $events argument to one of two DI services:
$events
DI service name
Provider class
Events
'on'
amiListener
MikoPBX\Common\Providers\AmiConnectionListener
Subscribed
'off'
amiCommander
MikoPBX\Common\Providers\AmiConnectionCommand
Suppressed
Both providers register a shared service that connects to 127.0.0.1:{AMI_PORT} and logs in as the internal phpagi user (the null username/secret fall through to the constructor defaults). amiCommander connects with events='off' so a command-only client is never blocked draining the event stream; amiListener connects with events='on' for code that needs to consume events.
Sending commands
Two low-level primitives back every action:
sendRequest(string $action, array $parameters = []): array— writes the action and blocks onwaitResponse()until aResponse:block arrives. Use for request/response actions.sendRequestTimeout(string $action, array $parameters = []): array— same, but transparently reconnects on a dead socket and returns[]on timeout. Used internally by the list/status helpers below. It auto-fills anActionIDof"{$action}_" . getmypid().
The raw CLI bridge:
Command(string $command, ?string $actionid = null): array— runs an Asterisk CLI command (theCommandAMI action), e.g.core show channels,pjsip show endpoints.
Channel and call operations
GetChannels(bool $group = true): array wraps the CoreShowChannels action. With $group = true (default) it returns channels grouped by Linkedid; with false it returns a flat list of channel names.
Originate a call from extension 201 to 203 (the BlackList module might do this to ring an administrator):
PJSIP helpers
These call PJSIP manager actions and normalize the multi-event responses into plain arrays:
getPjSipRegistry() issues PJSIPShowRegistrationsOutbound and returns rows of id, state, host, username for each outbound provider registration.
Queues
Call recording
MixMonitor records both legs of a call into a single mixed file and is the preferred recorder; Monitor records the legs separately. MikoPBX uses MixMonitor for standard call recording.
Working with events
Register handlers, then enter a blocking loop that dispatches each incoming event to the matching handler.
addEventHandler($event, $callback)registers a handler.$eventis lower-cased internally, so'Hangup'and'hangup'are equivalent;'*'is a catch-all.$callbackmust be an array callable[$object, 'method'](or a plain function-name string) — see the warning below. It returnsfalseif a handler for that event is already registered.Events(string $eventMask)turns the event stream on/off at runtime ('on','off', or a mask such as'system,call,log').UserEvent(string $name, array $headers)emits a customUserEvent— the standard way for an AGI script or worker to push a message onto the AMI bus.waitUserEvent()is a blocking loop tuned forUserEventconsumers; it also supports an idle callback (setOnIdleCallback()).
A minimal event-listening loop, of the kind a long-running worker uses. Note that the handlers are methods on the worker object, registered as array callables:
manager.conf — how AMI is configured
The manager.conf file is generated by MikoPBX\Core\Asterisk\Configs\ManagerConf.
Key facts to internalize:
The
[general]section (withenabled = yesandport = {AMI_PORT}) is written unconditionally. The AMI listener is always on.PbxSettings::AMI_PORT(keyAMIPort, default5038) sets the TCP port.PbxSettings::AMI_ENABLED(keyAMIEnabled, default1) gates only the block that writes the database-definedAsteriskManagerUsers. When it is'1', each enabled manager user becomes amanager.confsection with itssecret,permit/denyACLs, and read/write class permissions. The internalphpagiuser is always appended regardless of this setting.webenabled = yesis what allows the same manager actions to be reached over HTTP (AJAM).
Module hook: generateManagerConf
A module can append its own manager.conf sections by implementing the generateManagerConf hook. ManagerConf collects every module's contribution via the hook constant AsteriskConfigInterface::GENERATE_MANAGER_CONF:
So BlackListConf could expose a dedicated manager user by returning an extra section:
The generateManagerConf hook constant and the generateConfig() → generateConfigProtected() → saveConfig() flow are documented with the other hooks in the module class reference.
After regeneration, ManagerConf::reload() rewrites both manager.conf and http.conf and runs module reload manager / module reload http in Asterisk.
AMI users: AsteriskManagerUsers
Database-defined AMI accounts are the model MikoPBX\Common\Models\AsteriskManagerUsers, backed by table m_AsteriskManagerUsers. Each row carries the username, secret, a networkfilterid (linking to a NetworkFilters row that provides the permit/deny ACLs), per-class permissions (call, cdr, originate, reporting, agent, config, dialplan, dtmf, log, system, command, verbose, user, each read/write/readwrite/none), and optional eventfilter lines. Setting networkfilterid to localhost forces a localhost-only ACL.
http.conf — how AJAM is configured
AJAM rides on the Asterisk built-in HTTP server, configured by MikoPBX\Core\Asterisk\Configs\HttpConf.
The HTTP server is enabled when either
AJAM_ENABLED(keyAJAMEnabled, default1) orARI_ENABLED(keyARIEnabled, default0) is on.PbxSettings::AJAM_PORT(keyAJAMPort, default8088) is the HTTP port;PbxSettings::AJAM_PORT_TLS(keyAJAMPortTLS, default8089) is the HTTPS port. When a TLS port is set,HttpConfprovisions the certificate viaSslCertificateService::prepareAsteriskCertificates('asterisk-http')and writestlsenable/tlsbindaddr/tlscertfile/tlsprivatekey.prefix=asteriskis what makes every AJAM URL start with/asterisk/— e.g./asterisk/rawmanand/asterisk/mxml. Keep this prefix in mind when craftingcurlcalls.
HttpConf::reload() regenerates http.conf and runs module reload http.
Raw protocol examples
These run from the PBX shell (connect via SSH first). They use the localhost-only internal account; replace ${PBX_HOST} with a remote address only if you have an AsteriskManagerUsers record permitting it.
Originate
Connect to the AMI socket:
Authenticate:
Originate a call from 201 to 203:
SIPADDHEADER — header added to the INVITE sent to extension
201.pt1c_cid — destination number.
ALLOW_MULTY_ANSWER — if
201has multiple registrations,SIPADDHEADERis sent to all contacts.
AJAM exposes the same actions over HTTP under the /asterisk/ prefix:
Build the call file:
Move it into the Asterisk outgoing spool directory (Asterisk processes and removes it). The spool root is the resolved media mount point (Directories::getDir(Directories::AST_SPOOL_DIR), i.e. <mountpoint>/mikopbx/astspool); on a typical USB-disk install the mount point is /storage/usbdisk1:
Redirect (blind transfer)
Forwarding without consultation:
PJSIP/201-000001 — channel to forward.
internal-transfer — always use this context for redirects.
203 — destination number.
The channel that was bridged with
PJSIP/201-000001is hung up.
Attended transfer
Call forwarding with consultation (raw Atxfer action — there is no AsteriskManager wrapper for it):
PJSIP/201-000001 — the channel that transfers the call; it is connected to
203for consultation.When
PJSIP/201-000001hangs up, the bridged channel is connected to203.
See also
Last updated
Was this helpful?