FreePBX - Start custom dialplan via API

Hello all,

I’m creating a little project with the following set-up:
An external server monitors some sensors I installed on various stuff.
When a sensor is triggered, I want to launch a call on the FreePBX to a fixed number and when answered, playback a prerecorded message.

The dialplan part for this is not that hard, just check if the call gets answered or not.
But I’m not finding the solution to launch the dialplan via a HTTP message.

Any ideas?

Although AMI has code that will allow you to do this via HTTP, that code is little used, and therefore not well maintained. You would be better off either creating a call file, or using the Originate action over the primary (TCP port 5038) AMI interface.

Examples of both should be widely available.

The best thing may be to set up an http server in the language of your choice. Copy a .call file to trigger the dialplan

Pseudo code example (untested) in node:

const http = require('http');
const fs = require('fs');
const src = /home/asterisk/
const dest = /var/spool/asterisk/outgoing/

// Define the port number to listen on
const port = 3000;

// Define the username and password for authentication
const auth = {
  username: 'username',
  password: 'password'

// Create the HTTP server
const server = http.createServer((req, res) => {
  // Check if the request method is POST
  if (req.method === 'POST') {
    // Check if the authentication headers are present and correct
    if (req.headers.authorization === `Basic ${Buffer.from(`${auth.username}:${auth.password}`).toString('base64')}`) {
      // Read the contents of the .call file and write them to a new file
      const fileCopy = fs.copyFile( src, dest)
      fileCopy.on('finish', () => {
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end('File copied successfully\n');
    } else {
      // Authentication failed
      res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Secure Area"' });
  } else {
    // Invalid request method
    res.writeHead(405, { 'Allow': 'POST' });
    res.end('Method Not Allowed\n');

// Start the server
server.listen(port, () => {
  console.log(`Server running on port ${port}`);

ARI is another option: Asterisk 17 Channels REST API - Asterisk Project - Asterisk Project Wiki

Although originates can be done with ARI, ARI wasn’t intended for cases where the ARI process is short running. Really the ARI process is a server, or at least has a coroutine relationship and should be running as long as Asterisk is running.

This Asterisk group thread on the relationship between AMI (etc.) stresses that ARI is not the new universal solution.

1 Like

You never, ever copy a call file because it could be read partially before the copying is completed and triggered incompletely. You always move call files into the outgoing spool.

Also the move must not be across filesystems (as might happen if you created it on /tmp and then moved it to /var/spool (not sure if the FreePBX distribution creates this situation). Only moves within a filesystem are atomic, on Linux.

How is /tmp on the server a different filesystem than /var/spool?

I don’t know how SNG7 sets things up, but /var/spool has to persist across reboots, but /tmp does not. As a result some systems may have /tmp on a tmpfs filesystem, as, for example, Debian does for /run and /run/lock. tmpfs space is taken from virtual memory, so uses RAM and swap file space.

It is also conceivable that someone would put one on spinning disks and the other on solid state “disk”, although I think that less likely.

One might want to exclude /tmp from RAID, for performance reasons, given that it won’t survive a reboot.

Actually the file system standard somewhat encourages putting /var outside the root file system, as well.


Thanks for all the input!
I created the following setup:

Created new user, with specific permissions (ssh, copy, move, chmod, chwon permissions).
Created a template callfile and put it in the user’s home dir.

Now I remotely start a script that copies and moves the file to /var/spool/asterisk/outgoing.

Works perfectly and exactly what I needed!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.