AGI CNAM Lookup Script

Integrations Asterisk 18+ -- Last reviewed 2026-03-29 agi cnam php integrations callerid Found this useful? Upvote it. ×

AGI CNAM Lookup Script

When inbound calls arrive from a SIP trunk, the caller ID number is usually present but the caller name is often blank. An AGI script can query an external CNAM database via HTTP, retrieve the caller's name, and set it on the channel before the call is answered.

Requirements

The AGI Script (cnam-lookup.agi)

Place this in /var/lib/asterisk/agi-bin/ and make it executable (chmod +x).

#!/usr/bin/php -q
<?php
require_once('/var/lib/asterisk/agi-bin/phpagi-2.20/phpagi.php');

$agi = new AGI();

// CNAM provider credentials
$cnam_server = 'https://api.example.com/cnam';
$cnam_userid = 'your_user_id';
$cnam_secret = 'your_secret';

$cid_num  = trim($agi->request['agi_callerid']);
$cid_name = trim($agi->request['agi_calleridname']);

// If we already have a name, nothing to do
if ($cid_name !== '' && $cid_name !== $cid_num) {
    exit;
}

// --- Number normalization ---

// Strip leading +1 (12 digits like +12125551234)
if (strlen($cid_num) == 12 && substr($cid_num, 0, 2) === '+1') {
    $cid_num = substr($cid_num, 2);
}

// Strip leading 1 (11-char string like 12125551234)
if (strlen($cid_num) == 11 && substr($cid_num, 0, 1) === '1') {
    $cid_num = substr($cid_num, 1);
}

// Strip leading + (11-char string like +2125551234)
if (strlen($cid_num) == 11 && substr($cid_num, 0, 1) === '+') {
    $cid_num = substr($cid_num, 1);
}

// --- Handle special cases ---

if (strcasecmp($cid_num, 'anonymous') === 0) {
    $agi->set_callerid('"Anonymous" <Anonymous>');
    exit;
}

if (strcasecmp($cid_num, 'restricted') === 0) {
    exit;  // Leave as-is
}

if (!is_numeric($cid_num)) {
    $agi->set_callerid('"Unavailable" <>');
    exit;
}

// Not a valid NANPA 10-digit number
if (strlen($cid_num) != 10 || $cid_num < 2010000000 || $cid_num > 9899999999) {
    $agi->set_callerid('"Out of Area" <' . $cid_num . '>');
    exit;
}

// --- CNAM HTTP lookup ---

$url = "$cnam_server?userid=$cnam_userid&secret=$cnam_secret&number=$cid_num";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
curl_setopt($ch, CURLOPT_TIMEOUT, 4);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$cid_name = trim(curl_exec($ch));
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// If lookup failed, let the call through without a name
if ($cid_name === '' || $http_code !== 200) {
    exit;
}

$agi->set_callerid('"' . $cid_name . '" <' . $cid_num . '>');
?>

Dialplan Integration

[from-trunk]
; Run CNAM lookup before routing the call
exten => _X.,1,AGI(cnam-lookup.agi)
 same => n,Goto(internal,${EXTEN},1)

How it works

  1. AGI (Asterisk Gateway Interface): AGI lets Asterisk hand control of a channel to an external script. The script communicates with Asterisk over stdin/stdout using a simple text protocol. The phpagi library wraps this protocol in PHP objects.
  2. Number normalization: Inbound caller IDs arrive in inconsistent formats: +12125551234, 12125551234, 2125551234. The script strips prefixes to produce a consistent 10-digit NANPA number before querying the CNAM provider.
  3. Fail-open design: If the HTTP lookup times out (2-second connect, 4-second total) or returns empty, the script exits without changing the caller ID. The call proceeds with whatever caller ID it had. This prevents a slow CNAM service from blocking inbound calls.
  4. set_callerid(): Sets both the caller name and number in the format "Name" <Number>. This updates CALLERID(name) and CALLERID(num) on the channel for the duration of the call.
  5. Special caller IDs: Anonymous, restricted, non-numeric, and out-of-range numbers are handled before the HTTP lookup to avoid wasting API calls on numbers that won't return useful results.

Tips

User Notes

No notes yet. Be the first to contribute a tip or example.

Contribute a note

Share a tip, gotcha, or practical example. Keep it under 2000 characters. No questions (use the Asterisk community forums for support). Wrap code in backticks.

Moderated before publishing. Email never shown.

Related Snippets