Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
AMI Protocol Notes
DEPRECATION NOTICE: This is old documentation relevant to Adhearsion 1.x and will soon be removed. See the main documentation for up-to-date info.
Creating a robust and complete abstraction of the Asterisk Manager Interface is a very challenging task indeed. Within Asterisk, the API for a module to send responses or events is almost too simple: each module writes a raw string to the socket. Though the modules are expected to conform to the protocol's vague specification, not all do. The result is a truly horrendous, vaguely-structured, non-deterministic protocol which is extremely difficult to parse and requires many workarounds for the idiosyncrasies. Fortunately, Adhearsion's Ragel-based AMI parsing library takes most of that headache away from its users. The rest of this document is a public service for anyone who may try to implement an AMI parser in the future.
##High-level protocol details##
The Asterisk Manager Interface is a plain-text protocol in which key/value pairs are sent and received in stanzas. Each pair is separated by a colon, each line is separated by
\r\n and each stanza is separated by
\r\n\r\n. The simplest example of a sent action would be this:
Action: Status\r\n \r\n
Note: the special characters are included escaped to be perfectly explicit. A visual line break has been added after each
\n for clarity's sake.
To this action Asterisk should respond with
Response: Success\r\n Message: <some message here>\r\n Header1: Value1\r\n Header2: Value2\r\n \r\n
...where Header1/Header2/etc are just placeholders. Not all responses have a "Message" header.
When a new connection sends a "Login" action, it can optionally specify whether it wishes to receive internal Asterisk events on the socket. Example events include hangup events, channel creation events, application execution events, and, in some cases, even events for when a party stops or starts talking.
Events are sent on the socket in real-time as they occur within Asterisk. They follow the following format:
Event: EventNameHere\r\n Header1: Value1\r\n \r\n
An event or action can have any number of unique headers.
If this was all there was to the AMI protocol, it'd indeed be very easy to parse. However, because there was never a protocol abstraction mechanism within Asterisk, many nuances emerged over time with which we are unfortunately stuck. An implementer of an AMI library must identify and properly parse all of these edge cases.
When sending an action, the AMI client can optionally send a unique "ActionID" header along, which - in theory - should be sent back with the response.
Because there exists no protocol-enforcing abstraction within Asterisk, some developers succeeded in committing code which broke the AMI protocol's definition entirely. For example, the Queues application has the following response in some cases:
>>Action: Queues\r\n >>ActionID: 1226459191.11838\r\n >>\r\n <<No queues.
Note: >> denotes data we send and << denotes data we receive
Because the content of many AMI responses are generated deep within Asterisk functions, not all responses are properly delimited with "\r\n". Even as some of these get fixed, others are introduced. Be careful to watch for lines ending simply in "\n".
"Causal" events are AMI's version of a multi-part response. This is not a term you will find in the protocol; instead this is what we have chosen to call them as they require special treatment by the parser.
For example: when requesting a list of SIP peers you will get an immediate response indicating that the request was processed and that further information is coming. Then a series of events will follow, with each event indicating one entry in the result set. Finally, a special event known to Adhearsion as the "Causal Event Terminator" will signify the end of the result set.
What is unknown is whether Asterisk will ever interleave other events within the stream of Causal event responses. This has never been observed in the wild, but may still be possible.
Here is an example of a complete Causal event request and response (note again that ">>>" indicates data sent to Asterisk while "<<<" is data received from Asterisk):
>>>Action: coreshowchannels >>>ActionID: umtLtvSg-RN5n-GEay-Z786-YdiaSLNXkcYN <<<Response: Success <<<ActionID: umtLtvSg-RN5n-GEay-Z786-YdiaSLNXkcYN <<<EventList: start <<<Message: Channels will follow <<<Event: CoreShowChannel <<<ActionID: umtLtvSg-RN5n-GEay-Z786-YdiaSLNXkcYN <<<Channel: SIP/127.0.0.1-00000013 <<<UniqueID: 1287686437.19 <<<Context: adhearsion <<<Extension: 23432 <<<Priority: 2 <<<ChannelState: 6 <<<ChannelStateDesc: Up <<<Application: AGI <<<ApplicationData: agi://localhost:1050/default <<<CallerIDnum: blink <<<Duration: 00:00:00 <<<AccountCode: <<<BridgedChannel: <<<BridgedUniqueID: <<<Event: CoreShowChannel <<<ActionID: umtLtvSg-RN5n-GEay-Z786-YdiaSLNXkcYN <<<Channel: SIP/127.0.0.1-00000012 <<<UniqueID: 1287686427.18 <<<Context: adhearsion <<<Extension: 23432 <<<Priority: 2 <<<ChannelState: 6 <<<ChannelStateDesc: Up <<<Application: Playback <<<ApplicationData: demo-congrats <<<CallerIDnum: blink <<<Duration: 00:00:09 <<<AccountCode: <<<BridgedChannel: <<<BridgedUniqueID: <<<Event: CoreShowChannel <<<ActionID: umtLtvSg-RN5n-GEay-Z786-YdiaSLNXkcYN <<<Channel: SIP/127.0.0.1-00000011 <<<UniqueID: 1287686417.17 <<<Context: adhearsion <<<Extension: 23432 <<<Priority: 2 <<<ChannelState: 6 <<<ChannelStateDesc: Up <<<Application: Playback <<<ApplicationData: demo-congrats <<<CallerIDnum: blink <<<Duration: 00:00:19 <<<AccountCode: <<<BridgedChannel: <<<BridgedUniqueID: <<<Event: CoreShowChannelsComplete <<<EventList: Complete <<<ListItems: 3 <<<ActionID: umtLtvSg-RN5n-GEay-Z786-YdiaSLNXkcYN
One place you can still get in trouble is that the terminator event may not have the same name as the request. Most terminator names can be determined by taking the action name ("CoreShowChannels" in this example) and adding the word "Complete". There are exceptions to this however. The most common exception is "SIPPeers" and "IAXPeers", both of which are terminated with "PeerListComplete". This information is current as of Asterisk 1.6.2, but may have changed to be consistent with the other commands by Asterisk 1.8.
One other notable exception is that the causal command "DBGet", introduced around Asterisk 1.6, does not send a causal event terminator at all. This issue is fixed in Asterisk 1.8.