API
Overview of MikoPBX APIs and integration surfaces — REST API v3, AMI/AJAM, AGI, and the legacy module REST callback — with guidance on when to use each.
MikoPBX exposes four distinct integration surfaces. They are not interchangeable: each one lives at a different layer of the stack, speaks a different protocol, and is invoked from a different place. Picking the wrong surface is the single most common mistake in module development — for example, polling the REST API in a loop to watch call state when you should be subscribing to AMI events.
This page is a decision guide. Read it first, then follow the link to the child page for the surface you need.
The four surfaces at a glance
HTTPS, JSON, attribute-routed
Core/src/PBXCoreREST/
Management & CRUD, external integrations, module endpoints
TCP 5038 (AMI) / HTTP (AJAM)
Core/src/Core/Asterisk/AsteriskManager.php
Real-time Asterisk events and live commands in workers
Dialplan-invoked PHP scripts
Core/src/Core/Asterisk/AGI.php, agi-bin/
Per-call, in-dialplan decision logic
Module REST callback (legacy v1/v2)
HTTPS, JSON, route-table
module *Conf.php
Older modules; simple custom endpoints
REST API v3 — management and CRUD
The modern, recommended surface for anything that reads or writes configuration, manages resources, or is consumed by an external system (a CRM, a provisioning script, a mobile app, another server).
Routing is auto-discovered from PHP 8.4 attributes (
#[ApiResource],#[HttpMapping],#[ResourceSecurity]) byRouterProvider. You declare a resource class with attributes; no manual route table.Authentication is JWT Bearer tokens (HMAC-SHA256, 15-min access + 30-day refresh) or 64-char API keys, with
Resource:ActionRBAC. Requests from127.0.0.1/::1bypass authentication.URL shape is conventional REST:
GET /pbxcore/api/v3/{resource} → getList GET /pbxcore/api/v3/{resource}/{id} → getRecord POST /pbxcore/api/v3/{resource} → create PUT /pbxcore/api/v3/{resource}/{id} → update PATCH /pbxcore/api/v3/{resource}/{id} → patch DELETE /pbxcore/api/v3/{resource}/{id} → delete GET /pbxcore/api/v3/{resource}:custom → custom methodProcessing is asynchronous: the php-fpm front end queues the request on Redis (
api:requests) andWorkerApiCommands(3 parallel processes) executes the action and writes the result back. Every action returns aPBXApiResult:class PBXApiResult { public bool $success = false; public array $data; // initialised to [] in the constructor public array $messages; // ['error' => [...], 'warning' => [...]] public string $processor; // set by the Processor that ran public string $function; // the action name public ?int $httpCode = null; // 200, 201, 400, 422, 409, 500 public ?array $pagination = null; public string $reload = ''; }getResult()also injectspid(the worker PID) into the wire body.
Choose REST v3 when you are building a new module endpoint, exposing CRUD on a model, or integrating with anything outside the PBX. For ModuleBlackList you would add a v3 resource to manage rows in m_BlackListNumbers (add/remove/list blocked numbers from a CRM).
See the working example in Extensions/EXAMPLES/REST-API/ModuleExampleRestAPIv3/ (controllers under App/Controllers/, action logic under Lib/RestAPI/Tasks/). The module-authoring walkthrough is on the REST API in modules page.
→ Full reference: REST API v3
AMI / AJAM — real-time Asterisk events and commands
The Asterisk Manager Interface (AMI) is a long-lived TCP socket to Asterisk. It is how you watch the PBX live — channel state, dial events, queue membership, hangups — and how you send immediate control commands (originate a call, hang up a channel, redirect, add/remove a queue member).
Transport. AMI listens on TCP — port
5038by default, configured viaPbxSettings::AMI_PORT(seeCore/src/Core/Asterisk/Configs/ManagerConf.php, which generatesmanager.conf). The HTTP-flavoured variant AJAM is enabled bywebenabled = yesin the same[general]block and is served by Asterisk's built-in HTTP server (Core/src/Core/Asterisk/Configs/HttpConf.php,bindport=PbxSettings::AJAM_PORT, URL prefixasterisk).Client. Core wraps AMI in
MikoPBX\Core\Asterisk\AsteriskManager. Key methods:
AMI is for code that runs inside the PBX — typically a long-running module worker that holds the connection open and reacts to events. Do not poll the REST API in a loop to discover call state; subscribe to AMI events instead.
Choose AMI/AJAM when your module needs to react to live call activity. ModuleBlackList could run a worker that listens for incoming-call events and reacts in real time.
See the working example in Extensions/EXAMPLES/AMI/ModuleExampleAmi/ — note the dedicated worker Lib/WorkerExampleAmiAMI.php that owns the AMI connection.
→ Full reference: AMI / AJAM
AGI — in-call decision logic
The Asterisk Gateway Interface runs a script from the dialplan, during a call. Asterisk hands the script the channel and call variables; the script makes a decision and tells Asterisk what to do next (continue, set a variable, play a file, hang up). This is the right place for per-call logic that must execute inside the call flow.
Core's AGI client is
MikoPBX\Core\Asterisk\AGI(built onAGIBase). It exposes the standard AGI verbs as PHP methods:Real Core AGI scripts live in
Core/src/Core/Asterisk/agi-bin/(check_redirect.php,meetme_dial.php,unpark_call.php,get_park_info.php) — these are the canonical examples of how a dialplan-invoked script is structured. A module ships its own scripts in itsagi-bin/directory and wires them into the dialplan from its config class.
Choose AGI when you need a decision within a call before it is routed. ModuleBlackList’s natural fit: an AGI script invoked early in the incoming context that looks up the caller in m_BlackListNumbers and hangs up if matched. A module injects this into the dialplan via a hook like extensionGenInternal() on its BlackListConf (see the dialplan-generation hooks in Core/src/Core/Asterisk/Configs/AsteriskConfigInterface.php).
→ Full reference: AGI
Legacy module REST callback (v1 / v2)
Before REST API v3, modules exposed HTTP endpoints under /pbxcore/api/... in one of two ways, both declared in the module's config class. These still work and you will encounter them in existing modules, but new modules should use REST API v3.
v1 — single callback. The config class implements one method that dispatches on the action name:
See
Extensions/EXAMPLES/REST-API/ModuleExampleRestAPIv1/Lib/ExampleRestAPIv1Conf.php.v2 — explicit route table + controllers. The config class returns a route definition array mapping URLs to controller actions, supporting public (no-auth) routes:
See
Extensions/EXAMPLES/REST-API/ModuleExampleRestAPIv2/Lib/ExampleRestAPIv2Conf.php(routes like/pbxcore/api/module-example-rest-api-v2/{actionName}).
Use the legacy callback only when maintaining an existing v1/v2 module. For anything new, prefer attribute-routed REST API v3 — it gives you authentication, RBAC, OpenAPI generation, and the queue backpressure protection for free. The module-authoring guide covers all three patterns side by side: REST API in modules.
ARI — advanced call control (when AMI is not enough)
For programmatic media and call-control beyond what AMI/AGI offer (building channels, bridges, and stasis applications), Asterisk's REST Interface (ARI) is available. It is served over the same built-in HTTP server as AJAM — HttpConf enables the HTTP server when either AJAM or ARI is enabled — and modules can contribute to its configuration through the GENERATE_ARI_CONF hook on AsteriskConfigInterface. ARI is an advanced, specialised surface; most modules will not need it. Reach for it only when AMI events plus AGI decisions cannot express the call flow you need.
Decision summary
Last updated
Was this helpful?