How to Write a Custom AGI Script for VICIdial
AGI scripts let you run any external program during a live call to look up data, change routing, or set variables — here is how to write one from scratch for VICIdial.
VICIdial is built on Asterisk, and Asterisk ships with the Asterisk Gateway Interface (AGI) — a mechanism that lets a running call hand control to any external program, wait for it to finish, then continue the dialplan AGI (Asterisk Gateway Interface). You can write AGI scripts in Perl, Python, PHP, or any language that can read from standard input and write to standard output. VICIdial uses AGI heavily: scripts like CID_change.agi and agi-DID_route.agi are standard examples. Writing your own gives you call-time access to your own databases, CRM data, or routing logic that VICIdial's built-in options do not cover.
How the AGI protocol works
When Asterisk launches your script, it sends a block of environment variables over standard input — things like agi_callerid, agi_extension, agi_uniqueid, and agi_accountcode. These arrive as key: value pairs, one per line, followed by a blank line that signals the end of the header block. After that your script takes control and issues AGI commands — each one a line of text sent to standard output. Asterisk replies on standard input with a response code (200 for success) and the result value. A response of 200 result=1 usually means the command was accepted.
Decision flow inside a custom script
flowchart TD
A[Asterisk launches script] --> B[Read AGI header vars]
B --> C[Parse agi_callerid and agi_extension]
C --> D{Look up caller in DB}
D -->|Found| E[Set channel variable via SET VARIABLE]
D -->|Not found| F[Set default variable value]
E --> G[Send VERBOSE log line]
F --> G
G --> H[Exit script - return control to dialplan]A minimal working script in Perl
The VICIdial ecosystem uses Perl for most of its own AGI scripts, so Perl is the natural starting point. The pattern is always the same: read the header, parse the variables you need, run your logic, issue AGI commands, and exit. The script file goes in /var/lib/asterisk/agi-bin/ and must be executable (chmod 755).
#!/usr/bin/perl
use strict;
use warnings;
# Read the AGI header block
my %agi;
$| = 1; # disable output buffering
while (my $line = <STDIN>) {
chomp $line;
last if $line eq ''; # blank line ends header
if ($line =~ /^agi_(\w+):\s*(.*)$/) {
$agi{$1} = $2;
}
}
# Your logic goes here — e.g. look up the caller in your own DB
my $caller_id = $agi{callerid} // 'unknown';
my $campaign_id = $agi{accountcode} // '';
# Example: set a channel variable so the dialplan can branch on it
print "SET VARIABLE MY_LOOKUP_RESULT found\n";
my $response = <STDIN>; # read Asterisk's 200 result reply
# Log a line to the Asterisk console
print "VERBOSE \"custom-agi: caller=$caller_id campaign=$campaign_id\" 1\n";
<STDIN>;
exit 0;The key rule is $| = 1 (or its Python equivalent sys.stdout.flush() after every write). If you buffer output, Asterisk never sees the command and the call hangs while both sides wait for each other. Every command you send must be followed by reading the reply from standard input before you send another command — the protocol is strictly request-reply.
Useful AGI commands to know
Beyond SET VARIABLE and VERBOSE, the commands you will use most are GET VARIABLE to read a channel variable Asterisk already has, SET CALLERID to change the caller ID before the call reaches the trunk (the same thing CID_change.agi does internally), EXEC to run any Asterisk dialplan application, and HANGUP to end the call from the script if your logic decides it should not proceed. Each command and its arguments goes on a single line terminated by a newline character.
Wiring the script into VICIdial
Once the file is on the box, the cleanest place to call it is a Call Menu's Custom Dialplan Entry. In the VICIdial Admin panel, create a Call Menu, add a line like the one below to its Custom Dialplan Entry, and set the Phone Context on any phone that should use it to that Call Menu ID Call menu.
exten => _91NXXNXXXXXX,1,AGI(my-custom-lookup.agi,arg1---arg2)
exten => _91NXXNXXXXXX,n,GotoIf($[${MY_LOOKUP_RESULT} = found]?found_context,s,1)
exten => _91NXXNXXXXXX,n,Goto(default,${EXTEN},1)Arguments you pass on the dialplan line (separated by three dashes in VICIdial conventions, as used by CID_change.agi) arrive in your script's @ARGV array (Perl) or sys.argv[1:] (Python). Read them before you read the AGI header — they are passed as command-line arguments, not via standard input.
For a working reference, read the existing scripts in /var/lib/asterisk/agi-bin/ on your VICIdial box — particularly agi-DID_route.agi and agi-AGENT_route.agi. Both connect to the VICIdial MySQL database at call time using the credentials in astguiclient.conf, which is the pattern to follow for any script that needs to read or write campaign or lead data at call time API (application programming interface).
For a broader view of where custom AGI fits alongside API calls, see the VICIdial API and AGI overview. If your use case is changing caller ID specifically, the faster path is using the built-in CID_change.agi rather than writing your own.
If you want a VICIdial box where the AGI infrastructure is already in place so you can focus on writing logic rather than standing up Asterisk, every VICIfast plan delivers a production-ready server in under 40 seconds.
About VICIfast LLC
VICIfast LLC operates a managed VICIdial hosting + BYOI service for outbound and inbound call centers. We run the dialers, the carriers, the recordings pipeline, and the compliance plumbing so operators don’t have to.
Citing this article
VICIfast Engineering. “How to Write a Custom AGI Script for VICIdial”. VICIfast LLC, June 28, 2026. Retrieved from https://vicifast.com/blog/how-to-write-a-custom-agi
Have questions?
Related posts
You might be interested in
VICIfast newsletter
Liked this? Get the next one in your inbox.
We ship the kind of stuff you just read — concrete, numbers-first, no drip. One email when a new post goes live. Unsubscribe in one click.
Comments
No comments yet — be the first.