Skip to content

Voicemail Management Guide ​

This guide covers voicemail management in VueSIP using the useAmiVoicemail composable, enabling you to build applications with MWI (Message Waiting Indicator) monitoring, mailbox status tracking, and voicemail user management.

Overview ​

Voicemail is an essential feature for any business phone system. VueSIP provides comprehensive voicemail monitoring through the AMI (Asterisk Manager Interface), allowing you to:

  • Monitor MWI: Track new and old message counts for any mailbox
  • Real-time Updates: Receive instant notifications when messages arrive
  • Multi-mailbox Support: Monitor multiple mailboxes simultaneously
  • User Information: Retrieve voicemail user details and settings

Why Voicemail Management Matters:

  • User Experience: Users need to know when they have messages waiting
  • Productivity: Quick access to message counts reduces phone tag
  • Business Intelligence: Track voicemail usage patterns
  • Integration: Connect voicemail to unified messaging systems

Table of Contents ​


Prerequisites ​

Before using useAmiVoicemail, ensure you have:

  1. AMI WebSocket Proxy: An amiws proxy running alongside Asterisk
  2. AMI Permissions: User with voicemail-related permissions
  3. Asterisk Voicemail: Configured voicemail.conf with mailboxes
  4. VueSIP AMI Connection: Established AmiClient connection

Basic Setup ​

Creating the Voicemail Composable ​

typescript
import { computed } from 'vue'
import { useAmi, useAmiVoicemail } from 'vuesip'

// Get the AMI client
const ami = useAmi()

// Create the voicemail composable
const {
  mwiStates,           // Map of MWI states by mailbox
  mailboxes,           // Map of mailbox info
  isLoading,           // Loading state
  error,               // Error message if any
  totalNewMessages,    // Total new messages across all monitored mailboxes
  totalOldMessages,    // Total old messages
  hasWaitingMessages,  // Whether any mailbox has waiting messages

  // Methods
  getMwiState,         // Get MWI state for a mailbox
  getMailboxInfo,      // Get detailed mailbox info
  getVoicemailUsers,   // List all voicemail users
  refreshMailbox,      // Refresh MWI for a mailbox
  monitorMailbox,      // Start monitoring a mailbox
  unmonitorMailbox,    // Stop monitoring a mailbox
  clearMonitoring,     // Clear all monitoring
  onMwiChange,         // Listen for MWI changes
} = useAmiVoicemail(computed(() => ami.getClient()))

With Options ​

typescript
const voicemail = useAmiVoicemail(
  computed(() => ami.getClient()),
  {
    // Default voicemail context (default: 'default')
    defaultContext: 'default',

    // Use AMI events for real-time updates (default: true)
    useEvents: true,

    // Auto-refresh when client connects (default: true)
    autoRefresh: true,

    // Polling interval in ms (0 = disabled, default: 0)
    pollInterval: 0,

    // Callbacks
    onMwiChange: (mwi) => {
      console.log(`${mwi.mailbox}: ${mwi.newMessages} new messages`)
    },
    onNewMessage: (mailbox, count) => {
      showNotification(`New voicemail in ${mailbox}!`)
    },

    // Filter mailboxes
    mailboxFilter: (info) => info.context === 'sales',

    // Transform mailbox data
    transformMailbox: (info) => ({
      ...info,
      displayName: info.fullName || info.mailbox,
    }),
  }
)

Monitoring Mailboxes ​

Adding a Mailbox to Monitor ​

typescript
// Monitor a single mailbox
monitorMailbox('1001', 'default')

// Monitor multiple mailboxes
const extensions = ['1001', '1002', '1003']
extensions.forEach(ext => monitorMailbox(ext, 'default'))

When you monitor a mailbox, VueSIP:

  1. Adds it to the monitored set
  2. Fetches the initial MWI state
  3. Listens for MessageWaiting events for that mailbox

Stopping Monitoring ​

typescript
// Stop monitoring a specific mailbox
unmonitorMailbox('1001', 'default')

// Clear all monitoring
clearMonitoring()

Checking Monitored State ​

typescript
// Access all MWI states
for (const [key, mwi] of mwiStates.value) {
  console.log(`${key}: ${mwi.newMessages} new, ${mwi.oldMessages} old`)
}

// Check totals
console.log(`Total new: ${totalNewMessages.value}`)
console.log(`Has waiting: ${hasWaitingMessages.value}`)

MWI State ​

MWI State Structure ​

Each MWI state contains:

typescript
interface MwiState {
  mailbox: string       // Mailbox identifier (e.g., '1001@default')
  waiting: boolean      // Whether messages are waiting
  newMessages: number   // Count of new/unread messages
  oldMessages: number   // Count of old/read messages
  lastUpdated: Date     // When this state was last updated
  serverId?: number     // AMI server ID (for multi-server setups)
}

Getting MWI State ​

typescript
// Get MWI state (async - queries Asterisk)
const mwi = await getMwiState('1001', 'default')

console.log(`Mailbox: ${mwi.mailbox}`)
console.log(`Waiting: ${mwi.waiting}`)
console.log(`New: ${mwi.newMessages}`)
console.log(`Old: ${mwi.oldMessages}`)

Refreshing MWI ​

typescript
// Force refresh MWI for a mailbox
// This sends VoicemailRefresh action to Asterisk
await refreshMailbox('1001', 'default')

Voicemail Users ​

Listing All Users ​

typescript
// Get all voicemail users
const users = await getVoicemailUsers()

users.forEach(user => {
  console.log(`${user.mailbox} - ${user.fullName}`)
  console.log(`  New: ${user.newMessages}, Old: ${user.oldMessages}`)
  console.log(`  Email: ${user.email || 'Not configured'}`)
})

Filtering by Context ​

typescript
// Get users in a specific context
const salesUsers = await getVoicemailUsers('sales')

Getting Single User Info ​

typescript
// Get info for a specific mailbox
const info = await getMailboxInfo('1001', 'default')

if (info) {
  console.log(`Full Name: ${info.fullName}`)
  console.log(`Email: ${info.email}`)
  console.log(`Messages: ${info.newMessages} new, ${info.oldMessages} old`)
}

MailboxInfo Structure ​

typescript
interface MailboxInfo {
  mailbox: string         // Mailbox number
  context: string         // Voicemail context
  newMessages: number     // New message count
  oldMessages: number     // Old message count
  urgentMessages: number  // Urgent message count
  fullName?: string       // User's full name
  email?: string          // Email address for notifications
  pager?: string          // Pager number
  serverId?: number       // AMI server ID
}

Event Handling ​

Listening for MWI Changes ​

typescript
// Register a listener
const unsubscribe = onMwiChange((mwi) => {
  if (mwi.waiting) {
    showNotification(`You have ${mwi.newMessages} new messages`)
  }
})

// Later: unsubscribe when done
unsubscribe()

Using Callback Options ​

typescript
const voicemail = useAmiVoicemail(clientRef, {
  onMwiChange: (mwi) => {
    // Called for every MWI update
    updateBadge(mwi.mailbox, mwi.newMessages)
  },
  onNewMessage: (mailbox, count) => {
    // Called only when new messages arrive
    playNotificationSound()
  },
})

Advanced Usage ​

Building an MWI Badge Component ​

vue
<template>
  <div class="mwi-badge" :class="{ active: hasMessages }">
    <span class="icon">📬</span>
    <span v-if="hasMessages" class="count">{{ newCount }}</span>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useAmi, useAmiVoicemail } from 'vuesip'

const props = defineProps<{
  mailbox: string
  context?: string
}>()

const ami = useAmi()
const { mwiStates, monitorMailbox } = useAmiVoicemail(
  computed(() => ami.getClient())
)

// Start monitoring this mailbox
monitorMailbox(props.mailbox, props.context)

const mwiKey = computed(() =>
  `${props.mailbox}@${props.context || 'default'}`
)

const mwiState = computed(() =>
  mwiStates.value.get(mwiKey.value)
)

const hasMessages = computed(() =>
  mwiState.value?.waiting ?? false
)

const newCount = computed(() =>
  mwiState.value?.newMessages ?? 0
)
</script>

Multi-Mailbox Dashboard ​

vue
<template>
  <div class="voicemail-dashboard">
    <div class="summary">
      <span>Total: {{ totalNewMessages }} new messages</span>
    </div>

    <div class="mailbox-list">
      <div
        v-for="[key, mwi] in mwiStates"
        :key="key"
        class="mailbox-item"
        :class="{ 'has-new': mwi.newMessages > 0 }"
      >
        <span class="mailbox-name">{{ mwi.mailbox }}</span>
        <span class="message-counts">
          {{ mwi.newMessages }} new / {{ mwi.oldMessages }} old
        </span>
        <span class="updated">
          Updated: {{ formatTime(mwi.lastUpdated) }}
        </span>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted } from 'vue'
import { useAmiVoicemail } from 'vuesip'

const {
  mwiStates,
  totalNewMessages,
  monitorMailbox
} = useAmiVoicemail(clientRef)

// Monitor all extension mailboxes
onMounted(() => {
  const extensions = ['1001', '1002', '1003', '1004', '1005']
  extensions.forEach(ext => monitorMailbox(ext))
})

function formatTime(date: Date): string {
  return date.toLocaleTimeString()
}
</script>

Integration with Notifications ​

typescript
import { watch } from 'vue'

const { totalNewMessages, onMwiChange } = useAmiVoicemail(clientRef)

// Watch for new messages
watch(totalNewMessages, (newCount, oldCount) => {
  if (newCount > oldCount) {
    // New message arrived
    if ('Notification' in window && Notification.permission === 'granted') {
      new Notification('New Voicemail', {
        body: `You have ${newCount} new voicemail message(s)`,
        icon: '/voicemail-icon.png',
      })
    }
  }
})

// Or use the callback
onMwiChange((mwi) => {
  if (mwi.newMessages > 0) {
    // Update browser tab title
    document.title = `(${mwi.newMessages}) Voicemail`
  }
})

Asterisk Configuration ​

voicemail.conf Setup ​

ini
[general]
; Voicemail format
format=wav49|gsm|wav

; Email settings
serveremail=voicemail@yourserver.com
attach=yes
mailcmd=/usr/sbin/sendmail -t

; MWI settings
pollmailboxes=yes
pollfreq=30

[default]
; Extension mailboxes
1001 => 1234,John Doe,john@example.com,,attach=yes
1002 => 1234,Jane Smith,jane@example.com,,attach=yes
1003 => 1234,Bob Wilson,bob@example.com,,attach=yes

AMI User Permissions ​

ini
; manager.conf
[amiuser]
secret = your_secret
read = call,agent,user,system
write = call,agent,user,originate,system

Required Permissions ​

For voicemail features, your AMI user needs:

  • read: system (for VoicemailUsersList)
  • write: system (for VoicemailRefresh)

Best Practices ​

1. Monitor Only Needed Mailboxes ​

typescript
// Good: Monitor specific mailboxes
monitorMailbox(currentUserExtension)

// Avoid: Monitoring all mailboxes unnecessarily
// This generates more AMI traffic

2. Handle Connection Loss ​

typescript
import { watch } from 'vue'

watch(() => ami.isConnected.value, (connected) => {
  if (!connected) {
    // Connection lost - states may be stale
    console.log('AMI disconnected - voicemail states may be outdated')
  }
})

3. Clean Up on Unmount ​

typescript
import { onUnmounted } from 'vue'

onUnmounted(() => {
  clearMonitoring()
})

4. Use Computed Properties for UI ​

typescript
const displayMessage = computed(() => {
  if (totalNewMessages.value === 0) {
    return 'No new messages'
  }
  return `${totalNewMessages.value} new message${totalNewMessages.value > 1 ? 's' : ''}`
})

Released under the MIT License.