Voicemail and Call Recording
Activate this skill when the user is configuring voicemail.conf, delivering voicemail by email, recording calls with MixMonitor, deciding how long to keep recordings, or wiring recordings and messages into a transcription service on an Asterisk PBX. Triggers on "voicemail.conf," "VoiceMail," "VoiceMailMain," "MWI," "MixMonitor," "call recording," "record calls," "recording retention," "voicemail to email," "externnotify," "wav49," or "asterisk voicemail." Covers mailbox configuration, greetings and folders, email notification, MixMonitor options and post-processing, storage sizing and retention, consent and compliance notices, and transcription hooks.
You are a VoIP engineer who has run `app_voicemail` and `MixMonitor` on Asterisk 16 through 21 for call centres that record every call and for multi-tenant PBXs with thousands of mailboxes. You have recovered voicemail spools from full disks, been asked by lawyers for a specific recording from eighteen months ago, and built transcription pipelines before it was fashionable. You treat recordings as regulated data with a lifecycle, not as files that accumulate in `/var/spool`.
## Key Points
- **Name files so they can be found.** `${UNIQUEID}` and a timestamp in every recording filename; a database row mapping the recording to caller, callee, agent, queue and disposition.
- **Decide retention before the first file lands.** Disk fills silently; regulators and courts do not accept "we kept everything and lost it in a migration".
- **Consent is a configuration item.** The recording announcement, the ability to pause, and the jurisdiction rules are dialplan decisions made once and enforced everywhere.
- Voicemail retention: `maxmsg` per mailbox bounds it; a cron job aging `Old` folder messages past N days keeps spools bounded on abandoned mailboxes.
- Put `/var/spool/asterisk` on its own filesystem with monitoring. A full spool breaks voicemail and recording silently, with only `messages` log warnings to show for it.
- Employee notice: recording agents requires telling them; document it in the agent agreement and keep the `PAUSE` reason "private call" out of recorded queues.
- Access control: recordings readable only by the `asterisk` user and the retrieval application; every playback logged with who and why.
1. **MixMonitor post-command**: `rec-post.sh` pushes the file and `${UNIQUEID}` to a queue; a worker transcribes (a local model or a cloud API) and writes the text next to the CDR row.
- Spool on a monitored filesystem owned by `asterisk`
- `format` chosen deliberately; attachments tested on the mail clients people use
- `serveremail` and `mailcmd` verified with a test message reaching an external mailbox
- Timezone set per mailbox or in `[general]`; envelope times correctskilldb get asterisk-pbx-skills/voicemail-and-call-recordingFull skill: 168 linesVoicemail and Call Recording
You are a VoIP engineer who has run app_voicemail and MixMonitor on Asterisk 16 through 21 for call centres that record every call and for multi-tenant PBXs with thousands of mailboxes. You have recovered voicemail spools from full disks, been asked by lawyers for a specific recording from eighteen months ago, and built transcription pipelines before it was fashionable. You treat recordings as regulated data with a lifecycle, not as files that accumulate in /var/spool.
Core Philosophy
Voicemail and recording both produce audio that someone will want back later, under time pressure, with a reason attached. Design for the retrieval, not the capture:
- Name files so they can be found.
${UNIQUEID}and a timestamp in every recording filename; a database row mapping the recording to caller, callee, agent, queue and disposition. - Decide retention before the first file lands. Disk fills silently; regulators and courts do not accept "we kept everything and lost it in a migration".
- Consent is a configuration item. The recording announcement, the ability to pause, and the jurisdiction rules are dialplan decisions made once and enforced everywhere.
- Voicemail is email now. Most users listen from their inbox. Make the attachment play on a phone, the subject line informative, and the message deletable from the PBX once delivered if policy allows.
voicemail.conf
[general]
format = wav49|gsm|wav ; record all three; the first is used for the email attachment
serveremail = voicemail@example.com
fromstring = PBX Voicemail
attach = yes
maxmsg = 100
maxsecs = 180
minsecs = 3
maxgreet = 60
maxsilence = 8
silencethreshold = 128
maxlogins = 3
skipms = 3000
review = yes
operator = yes ; 0 during the greeting reaches the o extension
envelope = yes
saycid = yes
emaildateformat = %A, %d %B %Y at %H:%M
mailcmd = /usr/sbin/sendmail -t
emailsubject = New voicemail ${VM_MSGNUM} in mailbox ${VM_MAILBOX} from ${VM_CALLERID}
emailbody = Mailbox ${VM_MAILBOX} received a ${VM_DUR} message from ${VM_CALLERID} on ${VM_DATE}.\n
pbxskip = yes
tz = london
externnotify = /usr/local/bin/vm-notify.sh
pollmailboxes = yes ; needed when messages are removed outside Asterisk (IMAP clients, scripts)
pollfreq = 30
backupdeleted = 25
minpassword = 4
forcename = yes
forcegreetings = yes
[zonemessages]
london = Europe/London|'vm-received' Q 'digits/at' R
eastern = America/New_York|'vm-received' Q 'digits/at' IMp
[default]
101 => 4821,Alice Smith,alice@example.com,,attach=yes|delete=yes|tz=london
102 => 7719,Bob Jones,bob@example.com,bob-pager@example.com,attach=yes|saycid=yes
sales => 0000,Sales Group,sales@example.com,,attach=yes|delete=no|maxmsg=200
Mailbox lines are mailbox => password,name,email,pager,options with per-box options overriding [general]. delete=yes removes the message after the email is sent, which keeps the spool small but makes the mailbox useless from the phone; use it only for users who live in email. Multiple format values cost disk for every message; pick wav49 alone if attachments must play in every mail client, or wav if transcription quality matters more than size.
Dialplan Integration
exten => 101,1,Dial(PJSIP/101,20)
same => n,GotoIf($["${DIALSTATUS}"="BUSY"]?busy)
same => n,VoiceMail(101@default,u)
same => n,Hangup()
same => n(busy),VoiceMail(101@default,b)
same => n,Hangup()
exten => *97,1,VoiceMailMain(${CALLERID(num)}@default,s) ; own mailbox, skip password from own phone
exten => *98,1,VoiceMailMain(@default) ; prompt for mailbox and password
exten => o,1,Goto(queue-entry,operator,1) ; "press 0 for operator" lands here
exten => a,1,VoiceMailMain(${CALLERID(num)}@default) ; asterisk key during greeting
VoiceMail options: u unavailable greeting, b busy greeting, s skip instructions, d(context) let digits escape to a context, g(gain), U mark urgent, P mark priority. It sets ${VMSTATUS} to SUCCESS, USEREXIT or FAILED; FAILED with a mailbox that exists usually means the spool directory is not writable by the asterisk user. VoiceMailMain options: s skip password, p treat the mailbox argument as a prefix, a(folder) open a folder directly, g(gain).
${VM_INFO(101@default,count)} returns new-message count (add a folder as third argument); exists, fullname, email, tz are also readable. MWI lights reach phones through mailboxes = 101@default on the PJSIP endpoint (subscription-based) or aor (unsolicited NOTIFY); voicemail show users for default lists boxes with message counts.
Storage on disk is /var/spool/asterisk/voicemail/<context>/<mailbox>/<folder>/msg0000.txt plus one audio file per configured format. The .txt holds the envelope (callerid, origdate, origtime, duration, flag, msg_id). Folders are INBOX, Old, Work, Family, Friends, Urgent and Deleted (when backupdeleted is set). Greetings live beside them as unavail, busy, greet (recorded name) and temp. Asterisk 20 and later build ODBC and IMAP storage as separate modules (app_voicemail_odbc, app_voicemail_imap); pick one, never load two.
Call Recording With MixMonitor
MixMonitor(filename[,options[,command]]) records the channel it runs on, mixing both directions into one file, and keeps recording across transfers as long as the channel lives.
| Option | Effect |
|---|---|
b | Only record while the channel is bridged; drops ringing and IVR time |
a | Append to an existing file |
r(file) / t(file) | Additionally write received-only and transmitted-only files |
v(n), V(n), W(n) | Adjust received, transmitted and both volumes (-4 to 4) |
i(var) | Store the MixMonitor ID in a channel variable, for StopMixMonitor(id) when several run |
m(mailbox@context) | Copy the finished recording into a voicemail box |
p / P | Play a beep on the recording / non-recording side when starting |
The third argument is a shell command run when the recording finishes, with ${MIXMONITOR_FILENAME} expanded. A production pattern:
[sub-record]
exten => s,1,Set(REC_DIR=/var/spool/asterisk/monitor/${STRFTIME(${EPOCH},,%Y/%m/%d)})
same => n,System(mkdir -p ${REC_DIR})
same => n,Set(__REC_FILE=${REC_DIR}/${STRFTIME(${EPOCH},,%H%M%S)}-${FILTER(0-9,${CALLERID(num)})}-${ARG1}-${UNIQUEID}.wav)
same => n,Set(CDR(recordingfile)=${REC_FILE})
same => n,MixMonitor(${REC_FILE},bi(RECID),/usr/local/bin/rec-post.sh ${MIXMONITOR_FILENAME} ${UNIQUEID})
same => n,Return()
Call it with GoSub(sub-record,s,1(${EXTEN})) before Dial or Queue, or from a U() subroutine on the agent leg for per-agent recordings. CDR(recordingfile) becomes a real column with cdr_adaptive_odbc if the table has one, which is the join key the compliance team will ask for. StopMixMonitor(${RECID}) ends it early. The post-command runs in the recording thread after the file closes; keep rec-post.sh to "enqueue a job and exit" so a slow upload never blocks anything.
One-touch recording: set one_touch_recording = yes, record_on_feature = automixmon and record_off_feature = automixmon on the PJSIP endpoint and map automixmon => *1 in features.conf. Channel variables TOUCH_MIXMONITOR_FORMAT, TOUCH_MIXMONITOR_PREFIX and TOUCH_MIXMONITOR_OUTPUT control the filename. CLI: mixmonitor start PJSIP/101-00000012 file.wav, mixmonitor stop, mixmonitor list. From AMI, MixMonitorMute with Direction: both and State: 1 pauses capture without ending the file, which is the PCI pattern for card entry.
Storage, Retention and Sizing
- 8 kHz 16-bit
wavis 16 kB per second: about 57 MB per hour of recording, 1 GB per 17 hours.wav49is roughly one tenth. A 40-agent floor at 70 percent occupancy produces roughly 1.5 GB per day inwavbefore compression. - Record in
wavfor quality, then compress in the post-process job (lame -b 32 -m m in.wav out.mp3, or Opus withffmpeg -i in.wav -c:a libopus -b:a 24k out.opus) and move to object storage. Keep the compressed file's path in the database row. - Retention is a policy, enforced by a job:
find /var/spool/asterisk/monitor -type f -mtime +90 -deleteis the last resort; better is a job that reads the database, deletes what has expired, and records that it did. - Voicemail retention:
maxmsgper mailbox bounds it; a cron job agingOldfolder messages past N days keeps spools bounded on abandoned mailboxes. - Put
/var/spool/asteriskon its own filesystem with monitoring. A full spool breaks voicemail and recording silently, with onlymessageslog warnings to show for it.
Consent and Legal Notices
- Many jurisdictions require notifying or obtaining consent from all parties before recording; some US states require all-party consent, and the EU treats recordings as personal data requiring a lawful basis and a stated retention period. Financial regulation in the EU (MiFID II) requires firms to retain relevant recordings for years. Get the rule for each jurisdiction you terminate calls in from counsel, then encode it.
- Play the notice before recording starts:
Playback(custom/call-recorded)beforeMixMonitor, or placeMixMonitorin a subroutine that runs after the notice. For outbound campaigns the notice goes in theU()subroutine after answer. - Card payments: never keep sensitive authentication data. Pause with
MixMonitorMuteorStopMixMonitorwhile the caller enters a card number, or hand the caller to a payment IVR on a separate channel. - Employee notice: recording agents requires telling them; document it in the agent agreement and keep the
PAUSEreason "private call" out of recorded queues. - Access control: recordings readable only by the
asteriskuser and the retrieval application; every playback logged with who and why.
Transcription Hooks
Three points to attach a speech-to-text step:
- MixMonitor post-command:
rec-post.shpushes the file and${UNIQUEID}to a queue; a worker transcribes (a local model or a cloud API) and writes the text next to the CDR row. externnotifyinvoicemail.conf: Asterisk runs the script withcontext mailbox newcount oldcount urgentcountafter each new message. The script finds the newestmsgNNNN.wavinINBOX, transcribes it, and sends its own notification. Setattach = noandpbxskip = yesif the script replaces the built-in email.mailcmdpointed at your own program: Asterisk pipes the complete MIME email, attachment included, to stdin. The program decodes the audio, transcribes, appends the text to the body, and hands the result tosendmail. This keeps Asterisk's email templates and adds transcription with no change to mailbox handling.
Transcribe from the wav format, not wav49 or gsm; GSM compression measurably hurts recognition. For real-time transcription during the call, MixMonitor is the wrong tool; use ARI's external media channel to stream audio to a recogniser.
Checklist
- Spool on a monitored filesystem owned by
asterisk formatchosen deliberately; attachments tested on the mail clients people useserveremailandmailcmdverified with a test message reaching an external mailbox- Timezone set per mailbox or in
[general]; envelope times correct - MWI confirmed on a phone after leaving and deleting a message
- Recording filename includes
${UNIQUEID}and a timestamp; path stored in CDR boption on MixMonitor unless ring time must be captured- Consent notice before recording on every path (inbound, outbound, transfer)
- Retention job exists, runs, and logs what it deleted
- Pause mechanism for payments tested with a real call
Common Mistakes
MixMonitorin thehextension: the channel is already gone; recording must start before the bridge.- Recording Local channel legs without
/n: the optimisation swaps channels and the file is empty or cut short. - Two recordings of the same call because both the inbound context and the queue's
monitor-formatstart one; pick a single place. - Silence in the email attachment because
format = gsmproduced a file the mail client refuses to play. - Voicemail password equal to the extension number, harvested within days by scanners that then set call forwarding through the mailbox menu (
dialoutandcallbackgive the menu outbound reach; leave them unset unless needed). - Post-process commands that upload synchronously, tying up recording threads during outages.
- Deleting recordings by age without checking legal holds; the retention job must consult a hold list.
Limits and When Not to Use This
app_voicemail is a complete but dated system: no visual voicemail API beyond the spool files and AMI/ARI mailbox counts, and IMAP storage is fragile under load. Large multi-tenant deployments often replace it with a dialplan or ARI application recording to object storage and a real message store. MixMonitor records what one channel hears and says; for conference recording use ConfBridge's record_conference, and for lawful-intercept-grade capture with packet timing use res_hep or SIPREC via res_pjsip-adjacent tooling rather than mixed audio files. When recordings feed analytics at scale, stream them out at capture time rather than mining a spool directory later.
Install this skill directly: skilldb add asterisk-pbx-skills
Related Skills
AGI, ARI and AMI Integration
Activate this skill when the user needs external code to control or observe an Asterisk PBX: a database dip during a call, a custom IVR or dialer written in a real language, a wallboard, click-to-call, or agent state control. Triggers on "AGI," "FastAGI," "ARI," "Stasis," "AMI," "manager.conf," "ari.conf," "Originate," "asterisk REST," "asterisk websocket," "asterisk events," or "asterisk integration." Covers choosing between the three interfaces, authentication for each, a small working example of each, and the performance mistakes that take production systems down.
Asterisk Architecture and Installation
Activate this skill when the user is standing up an Asterisk PBX for the first time, choosing between source and package installs, or trying to understand how channels, the dialplan, applications and modules fit together. Triggers on "asterisk," "Asterisk PBX," "install asterisk," "menuselect," "asterisk from source," "asterisk CLI," "modules.conf," "asterisk.conf," "/etc/asterisk," "chan_pjsip," or "asterisk directory layout." Covers the core architecture, Debian and RHEL installs, menuselect choices, the directory tree, and the checks to run on first boot.
Asterisk Security Hardening
Activate this skill when the user is exposing an Asterisk PBX to the internet, has just been hit by toll fraud, or wants to lock down SIP registration, the dialplan, AMI and ARI before an audit. Triggers on "asterisk security," "toll fraud," "fail2ban asterisk," "SIP brute force," "friendly-scanner," "permit deny," "acl.conf," "alwaysauthreject," "SRTP," "SIP TLS," "firewall RTP," "restrict international calls," or "Asterisk PBX hardening." Covers the fraud patterns actually seen in production, log-driven banning, ACLs, credential policy, dialplan class of service and concurrency limits, TLS and SRTP transport configuration, and firewall rules for SIP and the RTP range.
Call Queues and Agents
Activate this skill when the user is configuring queues.conf on an Asterisk PBX, choosing a ring strategy, managing agents who log in and out, tuning hold announcements, or explaining queue statistics to a call-centre manager. Triggers on "queues.conf," "app_queue," "ringall," "rrmemory," "leastrecent," "penalty," "AddQueueMember," "PauseQueueMember," "wrapuptime," "queue_log," "service level," "abandon rate," "queue show," or "asterisk call center." Covers strategies, penalties and queue rules, static and dynamic members, announcements, wrap-up, the queue_log format, the metrics that matter, and the bugs every queue deployment hits.
CDR, CEL and Reporting
Activate this skill when the user needs billing-grade call records from an Asterisk PBX: configuring cdr.conf with a CSV or ODBC backend, enabling CEL for per-event detail, adding custom fields to records, writing call reports in SQL, or reconciling minutes against a carrier invoice. Triggers on "CDR," "cdr.conf," "Master.csv," "cdr_adaptive_odbc," "CEL," "cel.conf," "linkedid," "billsec," "disposition," "call report," "ASR," "ACD," "carrier invoice," "CDR(userfield)," or "asterisk billing." Covers what each CDR field really means, backend configuration for CSV and ODBC, CEL event types and how to read them, custom variables, the SQL reports managers actually ask for, and a reconciliation method that survives rounding, time zones and Local channels.
Dialplan Programming
Activate this skill when the user is writing or debugging extensions.conf for an Asterisk PBX: routing inbound DIDs, building outbound rules, structuring contexts, or replacing Macro() with GoSub. Triggers on "asterisk dialplan," "extensions.conf," "pattern matching," "_NXXXXXX," "GoSub," "Dial options," "hangup handler," "priorities," "same => n," "include =>," "DIALSTATUS," or "dialplan reload." Covers contexts, extensions, priorities, pattern matching, variables and functions, subroutines, Dial behaviour and worked dialplans you can paste and adapt.