ADSIProg()

Applications -- Last reviewed 2026-06-13 dialplan application Found this useful? Upvote it. ×

Selective Call Recording

Recording every call is easy: add MixMonitor() to every outbound route. But most systems only need to record certain extensions (compliance, training, a specific sales line) and skip the rest. The trick is using Asterisk's built-in database to flag which extensions get recorded, so you can toggle it from the CLI without touching the dialplan.

On this page

How it works

AstDB stores a simple flag per extension. The dialplan checks that flag before every outbound call. Flag is 1, call gets recorded. Flag is missing, call goes through clean.

Setting up the dialplan

Add the check before Dial() in your outbound route:

[outbound]
exten => _9NXXNXXXXXX,1,Set(RECORD_FLAG=${DB(recording/${CALLERID(num)})})
same => n,GotoIf($["${RECORD_FLAG}" = "1"]?record:dial)
same => n(record),Set(SAFE_CID=${FILTER(0-9,${CALLERID(num)})})
same => n,MixMonitor(${SAFE_CID}-${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${EXTEN:1}.wav,b)
same => n(dial),Dial(PJSIP/${EXTEN:1}@trunk)
same => n,Hangup()

DB(recording/${CALLERID(num)}) reads the AstDB entry for this extension. If the value is 1, the call branches to the record label and starts MixMonitor() before dialing.

FILTER(0-9,${CALLERID(num)}) strips any non-digit characters before using the caller ID in the filename. International numbers often arrive with a leading + (e.g., +12125551234) which is valid in dialplan variables but can cause problems in filenames. ${EXTEN:1} is Asterisk's substring syntax: VAR:offset skips the first offset characters, so :1 strips the leading 9 outside-line prefix from the dialed number.

The b option pauses recording whenever the channel is not bridged, including while a call is on hold. Recording resumes automatically when the bridge is re-established. This prevents ringback and early media from appearing at the start of a file and avoids capturing hold silence mid-call. For compliance use cases, be aware that audio during hold is not captured when b is set.

After editing extensions.conf, reload the dialplan so the changes take effect:

asterisk -rx 'dialplan reload'

Toggling recording per extension

From the Asterisk CLI, enable recording for extension 200:

asterisk -rx 'database put recording 200 1'

Disable it:

asterisk -rx 'database del recording 200'

Check which extensions are being recorded:

asterisk -rx 'database show recording'

You get something like:

/recording/200                                    : 1
/recording/205                                    : 1
2 results found.

Any extension with a value of 1 is recorded. Extensions without an entry (or with any other value) are not.

Adding a feature code for agents

Agents can also toggle their own recording with a feature code:

[internal]
exten => *96,1,Set(CURRENT=${DB(recording/${CALLERID(num)})})
same => n,GotoIf($["${CURRENT}" = "1"]?off:on)
same => n(on),Set(DB(recording/${CALLERID(num)})=1)
same => n,Playback(beep)
same => n,Hangup()
same => n(off),Set(UNUSED=${DB_DELETE(recording/${CALLERID(num)})})
same => n,Playback(beep&beep)
same => n,Hangup()

Dial *96 to toggle. One beep means recording is on, two beeps means off. Choose your feature code carefully: *72 is the standard North American activation code for Call Forwarding All Calls and will conflict on any system that has it enabled.

Recording inbound calls too

The outbound route only catches calls going out. For inbound, add the same DB check before ringing the extension:

[internal]
exten => _2XX,1,Set(RECORD_FLAG=${DB(recording/${EXTEN})})
same => n,GotoIf($["${RECORD_FLAG}" = "1"]?record:dial)
same => n(record),Set(SAFE_CID=${FILTER(0-9,${CALLERID(num)})})
same => n,MixMonitor(${EXTEN}-inbound-${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${SAFE_CID}.wav,b)
same => n(dial),Dial(PJSIP/${EXTEN},30)
same => n,GotoIf($["${DIALSTATUS}" = "NOANSWER"]?vm)
same => n,Hangup()
same => n(vm),Voicemail(${EXTEN}@default,u)
same => n,Hangup()

Extension 200 now gets recorded in both directions if the flag is set. The GotoIf after Dial() prevents the caller from being sent to voicemail after a call that was already answered.

Where recordings are stored

MixMonitor() writes to /var/spool/asterisk/monitor/ by default, which gets messy fast. Organize by date:

same => n(record),Set(SAFE_CID=${FILTER(0-9,${CALLERID(num)})})
same => n,Set(RECDIR=/var/spool/asterisk/monitor/${STRFTIME(${EPOCH},,%Y/%m/%d)})
same => n,System(mkdir -p ${RECDIR})
same => n,MixMonitor(${RECDIR}/${SAFE_CID}-${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${EXTEN:1}.wav,b)

This creates a YYYY/MM/DD directory structure. Add a cron job to clean up old recordings:

# Delete recordings older than 90 days
0 3 * * * find /var/spool/asterisk/monitor -name "*.wav" -mtime +90 -delete

Compressing recordings

WAV files eat disk. A 10-minute call is about 10 MB. The easiest way to reduce that without any external tools is to record directly to GSM format by changing the filename extension from .wav to .gsm:

same => n,MixMonitor(${RECDIR}/${SAFE_CID}-${STRFTIME(${EPOCH},,%Y%m%d-%H%M%S)}-${EXTEN:1}.gsm,b)

Asterisk writes GSM 6.10 natively at about 1 MB per 10 minutes. GSM files play back fine in Asterisk but most desktop media players won't open them without a plugin.

If you need MP3 output for archival or a web player, compress off-peak with a shell script:

#!/bin/bash
# compress-recordings.sh
# Run via cron during off-peak: 0 2 * * * /usr/local/bin/compress-recordings.sh

MONITOR_DIR="/var/spool/asterisk/monitor"

find "$MONITOR_DIR" -name "*.wav" -mmin +60 | while read -r wavfile; do
    mp3file="${wavfile%.wav}.mp3"
    if [ ! -f "$mp3file" ]; then
        lame --quiet -b 16 --resample 8 "$wavfile" "$mp3file" && rm "$wavfile"
    fi
done

-b 16 --resample 8 encodes at a fixed 16 kbps bitrate resampled to 8 kHz, which matches telephone audio bandwidth and produces files around 1-2 MB per 10 minutes. Avoid LAME's VBR presets (-V) for telephone recordings: they are calibrated for full-bandwidth music and will produce unexpectedly large output from 8 kHz audio.

Recording laws vary. Some jurisdictions require only one party to know about the recording; others require everyone on the call to be notified. If you are in a two-party consent jurisdiction, play an announcement before recording:

same => n(record),Playback(this-call-may-be-monitored)
same => n,MixMonitor(...)

Check your local regulations. See the Call Recording Consent Laws by State reference for a state-by-state breakdown of one-party vs all-party consent requirements.

Common problems

Recordings are empty or one-sided. Check direct_media = no on your PJSIP endpoints; if RTP goes directly between phones, Asterisk never touches the audio and has nothing to record. Also verify the MixMonitor() filename uses a supported extension (.wav, .gsm, .ulaw, etc.). Run core show file formats from the Asterisk CLI to see the formats your build supports.

MixMonitor() silently fails. Target directory does not exist or is not writable by the Asterisk user. Check /var/log/asterisk/messages.

Recordings cut off mid-call. Disk full. Monitor the partition.

User Notes

Know a tip or gotcha for this topic? Share it below and help others.

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